From 6a7d9d6d71985f321507491e48667318b70356af Mon Sep 17 00:00:00 2001 From: Athul Date: Mon, 2 Mar 2026 13:38:40 +0530 Subject: [PATCH 1/2] UN-1798: Fix dashboard metrics - HITL, pages processed, sidebar nav MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix HITL import path: manual_review_v2 → pluggable_apps.manual_review_v2 - Fix HITL model: ManualReviewEntity → HITLQueue with _base_manager - Fix pages_processed: resolve org string identifier from UUID for PageUsage - Add HITL metrics to live_series endpoint and summary - Restore metrics dashboard sidebar nav removed by sidebar refactor (#1768) Co-Authored-By: Claude Opus 4.6 --- backend/dashboard_metrics/services.py | 62 +++++++++++++++---- backend/dashboard_metrics/views.py | 2 + .../navigations/side-nav-bar/SideNavBar.css | 8 +++ .../navigations/side-nav-bar/SideNavBar.jsx | 21 +++++++ 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/backend/dashboard_metrics/services.py b/backend/dashboard_metrics/services.py index 1942bd7aa..1a7ab3c4f 100644 --- a/backend/dashboard_metrics/services.py +++ b/backend/dashboard_metrics/services.py @@ -22,6 +22,20 @@ from dashboard_metrics.models import Granularity from unstract.core.data_models import ExecutionStatus +from account_v2.models import Organization + + +def _get_hitl_queue_model(): + """Get HITLQueue model if available (cloud-only). + + Returns None on OSS where manual_review_v2 is not installed. + """ + try: + from pluggable_apps.manual_review_v2.models import HITLQueue + + return HITLQueue + except ImportError: + return None def _get_usage_queryset(): @@ -109,6 +123,10 @@ def get_pages_processed( Sums pages_processed field grouped by time period. + Note: PageUsage.organization_id stores the Organization's string + identifier (organization.organization_id), NOT the UUID PK. + We look up the string identifier from the UUID passed by callers. + Args: organization_id: Organization UUID string start_date: Start of date range @@ -118,11 +136,17 @@ def get_pages_processed( Returns: List of dicts with 'period' and 'value' keys """ + try: + org = Organization.objects.get(id=organization_id) + org_identifier = org.organization_id + except Organization.DoesNotExist: + return [] + trunc_func = MetricsQueryService._get_trunc_func(granularity) return list( PageUsage.objects.filter( - organization_id=organization_id, + organization_id=org_identifier, created_at__gte=start_date, created_at__lte=end_date, ) @@ -457,6 +481,9 @@ def get_hitl_reviews( ) -> list[dict[str, Any]]: """Query HITL review counts from manual_review_v2. + Counts all HITLQueue records created in the date range, + regardless of their current state. + Returns empty list on OSS where manual_review_v2 is not installed. Args: @@ -468,15 +495,14 @@ def get_hitl_reviews( Returns: List of dicts with 'period' and 'value' keys """ - try: - from manual_review_v2.models import ManualReviewEntity - except ImportError: + HITLQueue = _get_hitl_queue_model() + if HITLQueue is None: return [] trunc_func = MetricsQueryService._get_trunc_func(granularity) return list( - ManualReviewEntity.objects.filter( + HITLQueue._base_manager.filter( organization_id=organization_id, created_at__gte=start_date, created_at__lte=end_date, @@ -496,6 +522,9 @@ def get_hitl_completions( ) -> list[dict[str, Any]]: """Query completed HITL reviews from manual_review_v2. + Counts HITLQueue records with state='approved' that were approved + within the date range (using approved_at timestamp). + Returns empty list on OSS where manual_review_v2 is not installed. Args: @@ -507,21 +536,20 @@ def get_hitl_completions( Returns: List of dicts with 'period' and 'value' keys """ - try: - from manual_review_v2.models import ManualReviewEntity - except ImportError: + HITLQueue = _get_hitl_queue_model() + if HITLQueue is None: return [] trunc_func = MetricsQueryService._get_trunc_func(granularity) return list( - ManualReviewEntity.objects.filter( + HITLQueue._base_manager.filter( organization_id=organization_id, - status="APPROVED", - modified_at__gte=start_date, - modified_at__lte=end_date, + state=HITLQueue.State.APPROVED, + approved_at__gte=start_date, + approved_at__lte=end_date, ) - .annotate(period=trunc_func("modified_at")) + .annotate(period=trunc_func("approved_at")) .values("period") .annotate(value=Count("id")) .order_by("period") @@ -595,6 +623,14 @@ def get_all_metrics_summary( r["value"] or 0 for r in cls.get_failed_pages(organization_id, start_date, end_date) ), + "hitl_reviews": sum( + r["value"] + for r in cls.get_hitl_reviews(organization_id, start_date, end_date) + ), + "hitl_completions": sum( + r["value"] + for r in cls.get_hitl_completions(organization_id, start_date, end_date) + ), } @staticmethod diff --git a/backend/dashboard_metrics/views.py b/backend/dashboard_metrics/views.py index 658f85856..e68f25636 100644 --- a/backend/dashboard_metrics/views.py +++ b/backend/dashboard_metrics/views.py @@ -774,6 +774,8 @@ def live_series(self, request: Request) -> Response: "etl_pipeline_executions": MetricsQueryService.get_etl_pipeline_executions, "llm_usage": MetricsQueryService.get_llm_usage_cost, "prompt_executions": MetricsQueryService.get_prompt_executions, + "hitl_reviews": MetricsQueryService.get_hitl_reviews, + "hitl_completions": MetricsQueryService.get_hitl_completions, } # Filter by specific metric if requested diff --git a/frontend/src/components/navigations/side-nav-bar/SideNavBar.css b/frontend/src/components/navigations/side-nav-bar/SideNavBar.css index 1c3a50007..d45c10070 100644 --- a/frontend/src/components/navigations/side-nav-bar/SideNavBar.css +++ b/frontend/src/components/navigations/side-nav-bar/SideNavBar.css @@ -174,3 +174,11 @@ .space-styles:hover .sidebar-antd-icon { color: white; } + +.sidebar-menu-tag { + margin-left: 6px; + font-size: 10px; + line-height: 16px; + padding: 0 4px; + border-radius: 4px; +} diff --git a/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx b/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx index d0783633d..8a02aede7 100644 --- a/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx +++ b/frontend/src/components/navigations/side-nav-bar/SideNavBar.jsx @@ -10,6 +10,7 @@ import { Layout, Popover, Space, + Tag, Tooltip, Typography, } from "antd"; @@ -19,6 +20,7 @@ import { useNavigate } from "react-router-dom"; import apiDeploy from "../../../assets/api-deployments.svg"; import ConnectorsIcon from "../../../assets/connectors.svg"; import CustomTools from "../../../assets/custom-tools-icon.svg"; +import DashboardIcon from "../../../assets/dashboard.svg"; import EmbeddingIcon from "../../../assets/embedding.svg"; import etl from "../../../assets/etl.svg"; import LlmIcon from "../../../assets/llm.svg"; @@ -454,6 +456,17 @@ const SideNavBar = ({ collapsed, setCollapsed }) => { unstractMenuItems[1].subMenu.unshift(dashboardSideMenuItem(orgName)); } + // Add metrics dashboard menu item (available for both OSS and cloud) + unstractMenuItems[1].subMenu.unshift({ + id: 2.0, + title: "Dashboard", + tag: "New", + description: "View platform usage metrics and analytics", + image: DashboardIcon, + path: `/${orgName}/metrics`, + active: globalThis.location.pathname.startsWith(`/${orgName}/metrics`), + }); + // If selectedProduct is verticals and menu is null, don't show any sidebar items const data = selectedProduct === "verticals" && menu === null @@ -702,6 +715,14 @@ const SideNavBar = ({ collapsed, setCollapsed }) => {
{el.title} + {el.tag && ( + + {el.tag} + + )} {el.description} From 4dcb3ec25131790ca0c3c90cfbea78440268203f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 08:31:46 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backend/dashboard_metrics/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/dashboard_metrics/services.py b/backend/dashboard_metrics/services.py index 1a7ab3c4f..96ab331eb 100644 --- a/backend/dashboard_metrics/services.py +++ b/backend/dashboard_metrics/services.py @@ -12,6 +12,7 @@ from typing import Any from account_usage.models import PageUsage +from account_v2.models import Organization from api_v2.models import APIDeployment from django.db.models import CharField, Count, OuterRef, Q, Subquery, Sum from django.db.models.functions import Cast, Coalesce, TruncDay, TruncHour, TruncWeek @@ -22,7 +23,6 @@ from dashboard_metrics.models import Granularity from unstract.core.data_models import ExecutionStatus -from account_v2.models import Organization def _get_hitl_queue_model():