# Copyright (c) 2023-present Plane Software, Inc. and contributors # SPDX-License-Identifier: AGPL-3.0-only # See the LICENSE file for details. from datetime import datetime, timedelta, date from django.utils import timezone from typing import Dict, Optional, List, Union, Tuple, Any from plane.db.models import User def get_analytics_date_range( date_filter: Optional[str] = None, start_date: Optional[str] = None, end_date: Optional[str] = None, ) -> Optional[Dict[str, Dict[str, datetime]]]: """ Get date range for analytics with current and previous periods for comparison. Returns a dictionary with current and previous date ranges. Args: date_filter (str): The type of date filter to apply start_date (str): Start date for custom range (format: YYYY-MM-DD) end_date (str): End date for custom range (format: YYYY-MM-DD) Returns: dict: Dictionary containing current and previous date ranges """ if not date_filter: return None today = timezone.now().date() if date_filter == "yesterday": yesterday = today - timedelta(days=1) return { "current": { "gte": datetime.combine(yesterday, datetime.min.time()), "lte": datetime.combine(yesterday, datetime.max.time()), } } elif date_filter == "last_7_days": return { "current": { "gte": datetime.combine(today - timedelta(days=7), datetime.min.time()), "lte": datetime.combine(today, datetime.max.time()), }, "previous": { "gte": datetime.combine(today - timedelta(days=14), datetime.min.time()), "lte": datetime.combine(today - timedelta(days=8), datetime.max.time()), }, } elif date_filter == "last_30_days": return { "current": { "gte": datetime.combine(today - timedelta(days=30), datetime.min.time()), "lte": datetime.combine(today, datetime.max.time()), }, "previous": { "gte": datetime.combine(today - timedelta(days=60), datetime.min.time()), "lte": datetime.combine(today - timedelta(days=31), datetime.max.time()), }, } elif date_filter == "last_3_months": return { "current": { "gte": datetime.combine(today - timedelta(days=90), datetime.min.time()), "lte": datetime.combine(today, datetime.max.time()), }, "previous": { "gte": datetime.combine(today - timedelta(days=180), datetime.min.time()), "lte": datetime.combine(today - timedelta(days=91), datetime.max.time()), }, } elif date_filter == "custom" and start_date and end_date: try: start = datetime.strptime(start_date, "%Y-%m-%d").date() end = datetime.strptime(end_date, "%Y-%m-%d").date() return { "current": { "gte": datetime.combine(start, datetime.min.time()), "lte": datetime.combine(end, datetime.max.time()), } } except (ValueError, TypeError): return None return None def get_chart_period_range( date_filter: Optional[str] = None, ) -> Optional[Tuple[date, date]]: """ Get date range for chart visualization. Returns a tuple of (start_date, end_date) for the specified period. Args: date_filter (str): The type of date filter to apply. Options are: - "yesterday": Yesterday's date - "last_7_days": Last 7 days - "last_30_days": Last 30 days - "last_3_months": Last 90 days Defaults to "last_7_days" if not specified or invalid. Returns: tuple: A tuple containing (start_date, end_date) as date objects """ if not date_filter: return None today = timezone.now().date() period_ranges = { "yesterday": ( today - timedelta(days=1), today - timedelta(days=1), ), "last_7_days": (today - timedelta(days=7), today), "last_30_days": (today - timedelta(days=30), today), "last_3_months": (today - timedelta(days=90), today), } return period_ranges.get(date_filter, None) def get_analytics_filters( slug: str, user: User, type: str, date_filter: Optional[str] = None, project_ids: Optional[Union[str, List[str]]] = None, ) -> Dict[str, Any]: """ Get combined project and date filters for analytics endpoints Args: slug: The workspace slug user: The current user type: The type of filter ("analytics" or "chart") date_filter: Optional date filter string project_ids: Optional list of project IDs or comma-separated string of project IDs Returns: dict: A dictionary containing: - base_filters: Base filters for the workspace and user - project_filters: Project-specific filters - analytics_date_range: Date range filters for analytics comparison - chart_period_range: Date range for chart visualization """ # Get project IDs from request if project_ids and isinstance(project_ids, str): project_ids = [str(project_id) for project_id in project_ids.split(",")] # Base filters for workspace and user base_filters = { "workspace__slug": slug, "project__project_projectmember__member": user, "project__project_projectmember__is_active": True, "project__deleted_at__isnull": True, "project__archived_at__isnull": True, } # Project filters project_filters = { "workspace__slug": slug, "project_projectmember__member": user, "project_projectmember__is_active": True, "deleted_at__isnull": True, "archived_at__isnull": True, } # Add project IDs to filters if provided if project_ids: base_filters["project_id__in"] = project_ids project_filters["id__in"] = project_ids # Initialize date range variables analytics_date_range = None chart_period_range = None # Get date range filters based on type if type == "analytics": analytics_date_range = get_analytics_date_range(date_filter) elif type == "chart": chart_period_range = get_chart_period_range(date_filter) return { "base_filters": base_filters, "project_filters": project_filters, "analytics_date_range": analytics_date_range, "chart_period_range": chart_period_range, }