Back to Blog

Dashboard Development Guide

Dashboards are the visualization layer of the coomia-dip platform, transforming Ontology data and metrics into interactive charts and reports. This guide covers building dashboards using YAML and the Python SDK, including layout design, data source binding, chart configuration, interactive linking, permission control, and real-time refresh.

CoomiaPublished on January 19, 20269 min read
Share this articleTwitter / X

Series: S12 Developer Tutorials · Article 10 | Level: Intermediate | Reading Time: 15 min

Dashboard Development Guide

#TL;DR

Dashboards are the visualization layer of the coomia-dip platform, transforming Ontology data and metrics into interactive charts and reports. This guide covers building dashboards using YAML and the Python SDK, including layout design, data source binding, chart configuration, interactive linking, permission control, and real-time refresh.

#1. Dashboard Overview

#1.1 Architecture

Code
┌─────────────────────────────────────┐
│         Dashboard Runtime           │
│      (Frontend - React/Vue)         │
├─────────────────────────────────────┤
│  Layout Engine │ Widget Registry    │
│  Filter Engine │ Theme Manager      │
└────────┬────────────────┬───────────┘
         │ gRPC/WebSocket │
         ▼                ▼
┌─────────────────┐ ┌────────────────┐
│ Control Layer   │ │ Data Layer     │
│ (Metric API)    │ │ (OQL Query)    │
└─────────────────┘ └────────────────┘

#1.2 Core Concepts

ConceptDescription
DashboardA container holding multiple Widgets
WidgetVisual components: charts, tables, metric cards
DataSourceData binding to metrics or OQL queries
FilterGlobal/local filters with linked interactions
LayoutGrid-based positioning system

#2. Creating a Dashboard

#2.1 YAML Declaration

YAML
# dashboards/sales_overview.yaml
name: sales_overview
display_name: Sales Overview Dashboard
description: Real-time display of core sales metrics and trends
category: sales
owner: sales-team
refresh_interval: 300

theme:
  primary_color: "#1890ff"
  chart_palette: ["#1890ff", "#52c41a", "#faad14", "#f5222d", "#722ed1"]

permissions:
  view: [sales-team, management]
  edit: [sales-admin]

filters:
  - name: time_range
    type: date_range
    default: last_30_days
    position: header
  - name: region
    type: select
    source: "FIND Region SELECT name, code ORDER BY name"
    multiple: true
    position: header
  - name: product_category
    type: select
    source: "FIND ProductCategory SELECT name, code"
    multiple: true
    position: header

layout:
  columns: 24
  row_height: 60

widgets:
  # Row 1: Core Metric Cards
  - name: total_revenue_card
    type: metric_card
    position: {x: 0, y: 0, w: 6, h: 2}
    config:
      metric: total_revenue
      format: "currency"
      comparison: month_over_month
      trend: true
      icon: dollar

  - name: order_count_card
    type: metric_card
    position: {x: 6, y: 0, w: 6, h: 2}
    config:
      metric: order_count
      format: "number"
      comparison: month_over_month
      icon: shopping-cart

  - name: avg_order_value_card
    type: metric_card
    position: {x: 12, y: 0, w: 6, h: 2}
    config:
      metric: avg_order_value
      format: "currency"
      comparison: month_over_month
      icon: bar-chart

  - name: customer_count_card
    type: metric_card
    position: {x: 18, y: 0, w: 6, h: 2}
    config:
      metric: active_customer_count
      format: "number"
      comparison: month_over_month
      icon: team

  # Row 2: Trend Chart and Pie Chart
  - name: revenue_trend
    type: line_chart
    position: {x: 0, y: 2, w: 16, h: 4}
    config:
      title: Revenue Trend
      data_source:
        metric: total_revenue
        granularity: day
        apply_filters: [time_range, region]
      series:
        - name: Current Period
          color: "#1890ff"
        - name: Previous Period
          type: comparison
          period: previous_period
          style: dashed
          color: "#d9d9d9"
      axes:
        x: { type: time, format: "MM-DD" }
        y: { type: value, format: "currency_short" }

  - name: region_pie
    type: pie_chart
    position: {x: 16, y: 2, w: 8, h: 4}
    config:
      title: Revenue by Region
      data_source:
        metric: total_revenue
        dimension: region
        apply_filters: [time_range]
      show_percentage: true
      show_legend: true

  # Row 3: Bar Chart and Ranking Table
  - name: product_bar
    type: bar_chart
    position: {x: 0, y: 6, w: 12, h: 4}
    config:
      title: Sales by Product Category
      data_source:
        metric: total_revenue
        dimension: product_category
        apply_filters: [time_range, region]
      orientation: horizontal
      sort: desc
      show_value: true

  - name: top_customers
    type: table
    position: {x: 12, y: 6, w: 12, h: 4}
    config:
      title: Top 10 Customers
      data_source:
        type: oql
        query: |
          FIND Customer
          INCLUDE
            TRAVERSE serves <- Project <- Order
            WHERE Order.status = 'completed'
            AGGREGATE SUM(Order.total_amount) AS revenue, COUNT(*) AS order_count
          SELECT Customer.name, Customer.tier, revenue, order_count
          ORDER BY revenue DESC
          LIMIT 10
        apply_filters: [time_range]
      columns:
        - { field: name, title: Customer Name, width: 200 }
        - { field: tier, title: Tier, width: 80, render: tag }
        - { field: revenue, title: Total Revenue, width: 120, format: currency }
        - { field: order_count, title: Orders, width: 80, align: center }

#2.2 Python API Creation

Python
from ontology_sdk import OntoPlatform
from ontology_sdk.dashboard import (
    DashboardBuilder, MetricCard, LineChart, PieChart,
    BarChart, Table, Filter, Layout
)

platform = OntoPlatform(
    control_plane_url="localhost:50051",
    data_plane_url="localhost:50052"
)

dashboard = (
    DashboardBuilder("sales_overview")
    .display_name("Sales Overview Dashboard")
    .category("sales")
    .refresh_interval(300)
    .filter(Filter.date_range("time_range", default="last_30_days"))
    .filter(Filter.select("region", source="FIND Region SELECT name, code", multiple=True))

    # Metric cards
    .widget(
        MetricCard("total_revenue_card")
        .metric("total_revenue")
        .format("currency")
        .comparison("month_over_month")
        .position(0, 0, 6, 2)
    )
    .widget(
        MetricCard("order_count_card")
        .metric("order_count")
        .format("number")
        .comparison("month_over_month")
        .position(6, 0, 6, 2)
    )

    # Trend line chart
    .widget(
        LineChart("revenue_trend")
        .title("Revenue Trend")
        .metric("total_revenue", granularity="day")
        .comparison_series("previous_period")
        .apply_filters(["time_range", "region"])
        .position(0, 2, 16, 4)
    )

    # Pie chart
    .widget(
        PieChart("region_pie")
        .title("Revenue by Region")
        .metric("total_revenue", dimension="region")
        .show_percentage(True)
        .position(16, 2, 8, 4)
    )

    # Bar chart
    .widget(
        BarChart("product_bar")
        .title("Sales by Product Category")
        .metric("total_revenue", dimension="product_category")
        .orientation("horizontal")
        .sort("desc")
        .position(0, 6, 12, 4)
    )

    # Table
    .widget(
        Table("top_customers")
        .title("Top 10 Customers")
        .oql_source("""
            FIND Customer
            INCLUDE TRAVERSE serves <- Project <- Order
            WHERE Order.status = 'completed'
            AGGREGATE SUM(Order.total_amount) AS revenue
            SELECT Customer.name, Customer.tier, revenue
            ORDER BY revenue DESC LIMIT 10
        """)
        .columns([
            {"field": "name", "title": "Customer Name"},
            {"field": "tier", "title": "Tier", "render": "tag"},
            {"field": "revenue", "title": "Total Revenue", "format": "currency"},
        ])
        .position(12, 6, 12, 4)
    )

    .build()
)

platform.dashboards.register(dashboard)
print(f"Dashboard registered: {dashboard.name}")

#3. Widget Types in Detail

#3.1 Metric Card

Python
card = (
    MetricCard("revenue_card")
    .metric("total_revenue")
    .format("currency")
    .comparison("month_over_month")
    .trend(True)
    .sparkline(True)
    .threshold(
        warning=1000000,
        danger=500000,
        direction="below"
    )
    .position(0, 0, 6, 2)
)

#3.2 Line Chart

Python
line = (
    LineChart("multi_metric_trend")
    .title("Multi-Metric Trend Comparison")
    .series("total_revenue", label="Revenue", color="#1890ff", y_axis="left")
    .series("order_count", label="Orders", color="#52c41a", y_axis="right")
    .series("avg_order_value", label="AOV", color="#faad14", y_axis="left")
    .dual_y_axis(left_label="Amount ($)", right_label="Count")
    .granularity("day")
    .apply_filters(["time_range"])
    .legend(position="top")
    .tooltip(shared=True)
    .position(0, 2, 24, 5)
)

#3.3 Map Chart

Python
from ontology_sdk.dashboard import MapChart

geo_map = (
    MapChart("revenue_map")
    .title("National Revenue Distribution")
    .map_type("world")
    .metric("total_revenue", dimension="region")
    .color_scale(["#e6f7ff", "#1890ff", "#003a8c"])
    .show_labels(True)
    .drill_down(enabled=True, levels=["country", "state", "city"])
    .position(0, 10, 24, 6)
)

#3.4 Gauge

Python
from ontology_sdk.dashboard import Gauge

completion_gauge = (
    Gauge("project_completion")
    .title("Overall Project Completion Rate")
    .metric("project_completion_rate")
    .format("percentage")
    .ranges([
        {"min": 0, "max": 60, "color": "#f5222d"},
        {"min": 60, "max": 80, "color": "#faad14"},
        {"min": 80, "max": 100, "color": "#52c41a"},
    ])
    .position(0, 16, 8, 4)
)

#4. Interactions and Linking

#4.1 Global Filter Linking

YAML
filters:
  - name: department
    type: cascade
    levels:
      - source: "FIND BusinessUnit SELECT name, code"
        label: Business Unit
      - source: "FIND Department WHERE business_unit = :parent SELECT name, code"
        label: Department
      - source: "FIND Team WHERE department = :parent SELECT name, code"
        label: Team
    cascade: true
    position: header

#4.2 Widget-to-Widget Interaction

Python
# Click pie chart region -> filter table data
dashboard_builder.interaction(
    source_widget="region_pie",
    event="click",
    target_widget="top_customers",
    action="filter",
    mapping={"region": "$clicked.dimension_value"}
)

# Select table row -> show detail panel
dashboard_builder.interaction(
    source_widget="top_customers",
    event="row_click",
    target_widget="customer_detail_panel",
    action="load_detail",
    mapping={"customer_rid": "$row.rid"}
)

#4.3 Drill-Down Analysis

Python
line_chart = (
    LineChart("revenue_trend")
    .title("Revenue Trend")
    .metric("total_revenue", granularity="month")
    .drill_down(
        levels=["month", "week", "day"],
        animation=True
    )
    .position(0, 2, 16, 4)
)

#5. Real-Time Dashboards

#5.1 WebSocket Real-Time Push

Python
from ontology_sdk.dashboard import RealtimeDashboard

realtime = (
    RealtimeDashboard("ops_monitor")
    .display_name("Operations Monitoring Screen")
    .refresh_mode("realtime")
    .websocket_config(
        url="ws://data-Layer:8081/ws/metrics",
        reconnect_interval=5
    )
    .widget(
        MetricCard("current_qps")
        .metric("realtime_request_rate")
        .format("number")
        .suffix("/s")
        .realtime(True)
        .position(0, 0, 6, 2)
    )
    .widget(
        LineChart("qps_trend")
        .title("Real-time QPS Trend")
        .metric("realtime_request_rate")
        .granularity("1m")
        .rolling_window("30m")
        .realtime(True)
        .position(0, 2, 24, 4)
    )
    .build()
)

#6. Dashboard Templates

#6.1 Built-in Templates

Python
# Create from template
dashboard = platform.dashboards.create_from_template(
    template="executive_overview",
    name="ceo_dashboard",
    config={
        "metrics": {
            "primary_revenue": "total_revenue",
            "primary_cost": "total_cost",
            "primary_count": "order_count",
        },
        "dimensions": {
            "region": "customer.region",
            "category": "product.category",
        }
    }
)

# List available templates
templates = platform.dashboards.list_templates()
for t in templates:
    print(f"{t.name}: {t.description} (Widgets: {t.widget_count})")

#6.2 Export as Template

Python
platform.dashboards.export_as_template(
    dashboard_name="sales_overview",
    template_name="sales_dashboard_template",
    parameterize=["metrics", "dimensions", "filters"]
)

#7. Permission Control

Python
from ontology_sdk.dashboard import DashboardPermission

platform.dashboards.set_permissions(
    "sales_overview",
    permissions=[
        DashboardPermission(role="sales-admin", level="edit"),
        DashboardPermission(role="sales-team", level="view"),
        DashboardPermission(role="management", level="view"),
        DashboardPermission(
            role="regional-manager",
            level="view",
            row_filter={"region": "$user.region"}  # Row-level data permission
        ),
    ]
)

#8. Export and Sharing

Python
# Export as PDF
pdf_bytes = platform.dashboards.export(
    "sales_overview",
    format="pdf",
    filters={"time_range": "2025-01-01,2025-03-31"},
    paper_size="A3",
    orientation="landscape"
)
with open("sales_report_Q1.pdf", "wb") as f:
    f.write(pdf_bytes)

# Generate share link
share_link = platform.dashboards.share(
    "sales_overview",
    expires_in="7d",
    password="optional-password",
    filters_locked=True
)
print(f"Share link: {share_link.url}")

# Schedule email reports
platform.dashboards.schedule_report(
    "sales_overview",
    schedule="0 9 * * 1",  # Every Monday at 9 AM
    format="pdf",
    recipients=["management@company.com"],
    subject="Weekly Sales Report"
)

#9. Complete Example: Project Management Dashboard

Python
from ontology_sdk import OntoPlatform
from ontology_sdk.dashboard import (
    DashboardBuilder, MetricCard, LineChart, BarChart,
    Table, PieChart, Filter, Gauge
)

platform = OntoPlatform(
    control_plane_url="localhost:50051",
    data_plane_url="localhost:50052"
)

dashboard = (
    DashboardBuilder("project_management")
    .display_name("Project Management Dashboard")
    .category("project")
    .refresh_interval(600)

    # Filters
    .filter(Filter.date_range("time_range", default="last_90_days"))
    .filter(Filter.select("department", source="FIND Department SELECT name"))
    .filter(Filter.select("status", options=["planning", "in_progress", "done", "paused"]))

    # Row 1: Core metrics
    .widget(MetricCard("active_projects").metric("active_project_count").position(0, 0, 6, 2))
    .widget(MetricCard("completion_rate").metric("project_completion_rate").format("percentage").position(6, 0, 6, 2))
    .widget(MetricCard("overdue_tasks").metric("overdue_task_count").threshold(danger=10).position(12, 0, 6, 2))
    .widget(MetricCard("team_velocity").metric("avg_team_velocity").position(18, 0, 6, 2))

    # Row 2: Status distribution + completion trend
    .widget(
        PieChart("status_distribution")
        .title("Project Status Distribution")
        .oql_source("FIND Project GROUP BY status AGGREGATE COUNT(*) AS count")
        .position(0, 2, 8, 4)
    )
    .widget(
        LineChart("completion_trend")
        .title("Task Completion Trend")
        .metric("completed_task_count", granularity="week")
        .comparison_series("previous_period")
        .position(8, 2, 16, 4)
    )

    # Row 3: Department workload + project list
    .widget(
        BarChart("dept_workload")
        .title("Department Task Load")
        .oql_source("""
            FIND Task WHERE status != 'done'
            TRAVERSE assigned_to -> Employee
            TRAVERSE works_in -> Department
            GROUP BY Department.name
            AGGREGATE COUNT(*) AS pending_tasks
        """)
        .position(0, 6, 10, 4)
    )
    .widget(
        Table("project_list")
        .title("Project Progress Details")
        .oql_source("""
            FIND Project
            WHERE status IN ('planning', 'in_progress')
            INCLUDE TRAVERSE has_task -> Task
            AGGREGATE COUNT(*) AS total, COUNT(CASE WHEN Task.status='done' THEN 1 END) AS done
            SELECT name, status, priority, end_date, total, done
            ORDER BY end_date ASC
        """)
        .columns([
            {"field": "name", "title": "Project Name", "width": 200},
            {"field": "status", "title": "Status", "render": "tag"},
            {"field": "priority", "title": "Priority", "render": "tag"},
            {"field": "end_date", "title": "Due Date"},
            {"field": "progress", "title": "Progress", "render": "progress_bar",
             "computed": "done / total * 100"},
        ])
        .position(10, 6, 14, 4)
    )

    .build()
)

platform.dashboards.register(dashboard)
url = platform.dashboards.get_url("project_management")
print(f"Dashboard URL: {url}")

#Key Takeaways

  1. Declarative first: YAML for layout and data binding, Python API for complex interactions
  2. Flexible data sources: Both metric queries and OQL queries as data sources
  3. Interactive linking: Global filters, widget-to-widget linking, and drill-down analysis create a complete analytics experience
  4. Real-time capability: WebSocket push enables second-level refresh for monitoring screens
  5. Layered permissions: Dashboard-level view/edit permissions plus row-level data permissions
  6. Share and export: PDF export, share links, and scheduled reports

#Next Article

Next: S12-11 Permission Configuration Guide — Learn coomia-dip's three-layer permission model: RBAC + ABAC + Row-Level Security.

Tags: Dashboard Visualization Charts Reports Real-time Monitoring coomia-dip