diff --git a/apiserver/plane/app/views/analytic/advance.py b/apiserver/plane/app/views/analytic/advance.py index 9b258eca0..127d22acb 100644 --- a/apiserver/plane/app/views/analytic/advance.py +++ b/apiserver/plane/app/views/analytic/advance.py @@ -1,9 +1,10 @@ from rest_framework.response import Response from rest_framework import status from typing import Dict, List, Any -from datetime import timedelta from django.db.models import QuerySet, Q, Count from django.http import HttpRequest +from django.db.models.functions import TruncMonth +from django.utils import timezone from plane.app.views.base import BaseAPIView from plane.app.permissions import ROLE, allow_permission @@ -15,14 +16,10 @@ from plane.db.models import ( Module, IssueView, ProjectPage, + Workspace ) -from django.db.models import ( - Q, - Count, -) from plane.utils.build_chart import build_analytics_chart -from datetime import timedelta from plane.bgtasks.analytic_plot_export import export_analytics_to_csv_email from plane.utils.date_utils import ( get_analytics_filters, @@ -125,7 +122,7 @@ class AdvanceAnalyticsEndpoint(AdvanceAnalyticsBaseView): def get_work_items_stats(self) -> Dict[str, Dict[str, int]]: - base_queryset = Issue.objects.filter(**self.filters["base_filters"]) + base_queryset = Issue.issue_objects.filter(**self.filters["base_filters"]) return { "total_work_items": self.get_filtered_counts(base_queryset), @@ -263,6 +260,7 @@ class AdvanceAnalyticsChartEndpoint(AdvanceAnalyticsBaseView): "assignees", "labels", "issue_module__module", "issue_cycle__cycle" ) ) + # Apply date range filter if available if self.filters["chart_period_range"]: start_date, end_date = self.filters["chart_period_range"] @@ -270,41 +268,54 @@ class AdvanceAnalyticsChartEndpoint(AdvanceAnalyticsBaseView): created_at__date__gte=start_date, created_at__date__lte=end_date ) - # Get daily stats with optimized query - daily_stats = ( - queryset.values("created_at__date") + # Annotate by month and count + monthly_stats = ( + queryset.annotate(month=TruncMonth("created_at")) + .values("month") .annotate( created_count=Count("id"), completed_count=Count("id", filter=Q(completed_at__isnull=False)), ) - .order_by("created_at__date") + .order_by("month") ) - # Create a dictionary of existing stats with summed counts + # Create dictionary of month -> counts stats_dict = { - stat["created_at__date"].strftime("%Y-%m-%d"): { + stat["month"].strftime("%Y-%m-%d"): { "created_count": stat["created_count"], "completed_count": stat["completed_count"], } - for stat in daily_stats + for stat in monthly_stats } - # Generate data for all days in the range + # Generate monthly data (ensure months with 0 count are included) data = [] - current_date = start_date - while current_date <= end_date: - date_str = current_date.strftime("%Y-%m-%d") + workspace = Workspace.objects.get(slug=self._workspace_slug) + start_date = workspace.created_at.date().replace(day=1) + # include the current date at the end + end_date = timezone.now().date() + last_month = end_date.replace(day=1) + current_month = start_date + + while current_month <= last_month: + date_str = current_month.strftime("%Y-%m-%d") stats = stats_dict.get(date_str, {"created_count": 0, "completed_count": 0}) data.append( { "key": date_str, "name": date_str, - "count": stats["created_count"] + stats["completed_count"], + "count": stats[ + "created_count" + ], # <- Total created issues in that month "completed_issues": stats["completed_count"], "created_issues": stats["created_count"], } ) - current_date += timedelta(days=1) + # Move to next month + if current_month.month == 12: + current_month = current_month.replace(year=current_month.year + 1, month=1) + else: + current_month = current_month.replace(month=current_month.month + 1) schema = { "completed_issues": "completed_issues", diff --git a/apiserver/plane/utils/date_utils.py b/apiserver/plane/utils/date_utils.py index 86e6b9a3e..5726fbfd1 100644 --- a/apiserver/plane/utils/date_utils.py +++ b/apiserver/plane/utils/date_utils.py @@ -129,7 +129,7 @@ def get_chart_period_range( "last_3_months": (today - timedelta(days=90), today), } - return period_ranges.get(date_filter, period_ranges["last_7_days"]) + return period_ranges.get(date_filter, None) def get_analytics_filters( @@ -165,6 +165,8 @@ def get_analytics_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 @@ -172,6 +174,8 @@ def get_analytics_filters( "workspace__slug": slug, "project_projectmember__member": user, "project_projectmember__is_active": True, + "project__deleted_at__isnull": True, + "project__archived_at__isnull": True, } # Add project IDs to filters if provided diff --git a/web/core/components/analytics-v2/analytics-filter-actions.tsx b/web/core/components/analytics-v2/analytics-filter-actions.tsx index b9b69bed9..aae166bf4 100644 --- a/web/core/components/analytics-v2/analytics-filter-actions.tsx +++ b/web/core/components/analytics-v2/analytics-filter-actions.tsx @@ -19,14 +19,14 @@ const AnalyticsFilterActions = observer(() => { }} projectIds={workspaceProjectIds} /> - { updateSelectedDuration(val); }} dropdownArrow - /> + /> */} ); }); diff --git a/web/core/components/analytics-v2/analytics-section-wrapper.tsx b/web/core/components/analytics-v2/analytics-section-wrapper.tsx index deb691644..2a3a17f6a 100644 --- a/web/core/components/analytics-v2/analytics-section-wrapper.tsx +++ b/web/core/components/analytics-v2/analytics-section-wrapper.tsx @@ -17,7 +17,7 @@ const AnalyticsSectionWrapper: React.FC = (props) => { {title && (

{title}

- {subtitle &&

• {subtitle}

} + {/* {subtitle &&

• {subtitle}

} */}
)} {actions} diff --git a/web/core/components/analytics-v2/insight-card.tsx b/web/core/components/analytics-v2/insight-card.tsx index cd22b7e92..2ce4c2fdc 100644 --- a/web/core/components/analytics-v2/insight-card.tsx +++ b/web/core/components/analytics-v2/insight-card.tsx @@ -30,12 +30,12 @@ const InsightCard = (props: InsightCardProps) => { {!isLoading ? (
{count}
- {percentage && ( + {/* {percentage && (
{versus &&
vs {versus}
}
- )} + )} */}
) : ( diff --git a/web/core/components/analytics-v2/insight-table/data-table.tsx b/web/core/components/analytics-v2/insight-table/data-table.tsx index c811c9265..61e02cb33 100644 --- a/web/core/components/analytics-v2/insight-table/data-table.tsx +++ b/web/core/components/analytics-v2/insight-table/data-table.tsx @@ -51,14 +51,10 @@ export function DataTable({ columns, data, searchPlaceholder, act rowSelection, columnFilters, }, - enableRowSelection: true, - onRowSelectionChange: setRowSelection, - onSortingChange: setSorting, onColumnFiltersChange: setColumnFilters, onColumnVisibilityChange: setColumnVisibility, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), - getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), getFacetedRowModel: getFacetedRowModel(), getFacetedUniqueValues: getFacetedUniqueValues(), diff --git a/web/core/components/analytics-v2/overview/project-insights.tsx b/web/core/components/analytics-v2/overview/project-insights.tsx index a767cf476..fa156be6a 100644 --- a/web/core/components/analytics-v2/overview/project-insights.tsx +++ b/web/core/components/analytics-v2/overview/project-insights.tsx @@ -34,7 +34,7 @@ const ProjectInsights = observer(() => { `radar-chart-${workspaceSlug}-${selectedDuration}-${selectedProjects}`, () => analyticsV2Service.getAdvanceAnalyticsCharts[]>(workspaceSlug, "projects", { - date_filter: selectedDuration, + // date_filter: selectedDuration, ...(selectedProjects?.length > 0 && { project_ids: selectedProjects?.join(",") }), }) ); diff --git a/web/core/components/analytics-v2/total-insights.tsx b/web/core/components/analytics-v2/total-insights.tsx index ac8914e11..09998feaf 100644 --- a/web/core/components/analytics-v2/total-insights.tsx +++ b/web/core/components/analytics-v2/total-insights.tsx @@ -26,7 +26,7 @@ const TotalInsights: React.FC<{ analyticsType: TAnalyticsTabsV2Base; peekView?: `total-insights-${analyticsType}-${selectedDuration}-${selectedProjects}`, () => analyticsV2Service.getAdvanceAnalytics(workspaceSlug, analyticsType, { - date_filter: selectedDuration, + // date_filter: selectedDuration, ...(selectedProjects?.length > 0 ? { project_ids: selectedProjects.join(",") } : {}), }) ); diff --git a/web/core/components/analytics-v2/work-items/created-vs-resolved.tsx b/web/core/components/analytics-v2/work-items/created-vs-resolved.tsx index bd4673f46..2c95b916d 100644 --- a/web/core/components/analytics-v2/work-items/created-vs-resolved.tsx +++ b/web/core/components/analytics-v2/work-items/created-vs-resolved.tsx @@ -28,7 +28,7 @@ const CreatedVsResolved = observer(() => { `created-vs-resolved-${workspaceSlug}-${selectedDuration}-${selectedProjects}`, () => analyticsV2Service.getAdvanceAnalyticsCharts(workspaceSlug, "work-items", { - date_filter: selectedDuration, + // date_filter: selectedDuration, ...(selectedProjects?.length > 0 && { project_ids: selectedProjects?.join(",") }), }) ); diff --git a/web/core/components/analytics-v2/work-items/priority-chart.tsx b/web/core/components/analytics-v2/work-items/priority-chart.tsx index acf0b6cf9..5e81c7efa 100644 --- a/web/core/components/analytics-v2/work-items/priority-chart.tsx +++ b/web/core/components/analytics-v2/work-items/priority-chart.tsx @@ -57,7 +57,7 @@ const PriorityChart = observer((props: Props) => { `customized-insights-chart-${workspaceSlug}-${selectedDuration}-${selectedProjects}-${props.x_axis}-${props.y_axis}-${props.group_by}`, () => analyticsV2Service.getAdvanceAnalyticsCharts(workspaceSlug, "custom-work-items", { - date_filter: selectedDuration, + // date_filter: selectedDuration, ...(selectedProjects?.length > 0 && { project_ids: selectedProjects?.join(",") }), ...props, }) diff --git a/web/core/components/analytics-v2/work-items/workitems-insight-table.tsx b/web/core/components/analytics-v2/work-items/workitems-insight-table.tsx index cd3e7ae4e..85c93676b 100644 --- a/web/core/components/analytics-v2/work-items/workitems-insight-table.tsx +++ b/web/core/components/analytics-v2/work-items/workitems-insight-table.tsx @@ -30,7 +30,7 @@ const WorkItemsInsightTable = observer(() => { `insights-table-work-items-${workspaceSlug}-${selectedDuration}-${selectedProjects}`, () => analyticsV2Service.getAdvanceAnalyticsStats(workspaceSlug, "work-items", { - date_filter: selectedDuration, + // date_filter: selectedDuration, ...(selectedProjects?.length > 0 ? { project_ids: selectedProjects.join(",") } : {}), }) );