Documentation

fsds/bbu-analytics-dashboards.md

BBU Analytics Dashboards via Superset API

Executive Summary

Create comprehensive Superset dashboards via REST API showcasing:

  1. Data Platform Overview - Volume metrics across all systems
  2. Shipment Lifecycle Tracking - Camera/RFID/basket correlation (the exciting new feature!)
  3. Operations Dashboard - Daily throughput and processing stats
  4. Data Quality Metrics - Processing success rates and dead letter analysis

Current State

Existing Assets

  • Database ID: 1 (Acsis PostgreSQL)
  • Datasets: 8 BBU views (created 30 days ago)
  • Charts: 4 (not assigned to any dashboard)
  • Dashboards: 0

Data Volumes (Live)

Entity Count
Passports 1,506,998
Identifiers 3,644,510
Catalog Items 862,591
Item Types 554
Stacks (from Oracle) 151,679
Bakery Baskets 25,853
Shipments 2,848
Deliveries 40,757
Shipment Allocations 973,362
RFID Tag Reads 70,688
Camera Reads 36,693
Basket Correlations 1,997

Shipment Lifecycle Metrics (New Feature!)

Metric Value
Stacks with basket correlation 127
Unique baskets linked 1,069
Total correlations 1,997
Avg baskets per stack 12.0
Match rate (TXT format) ~29%

API Configuration

Base URL: http://localhost:<port>  (check Aspire Dashboard for current port)
Auth: Bearer JWT token
Headers required:
  - Authorization: Bearer <token>
  - X-CSRFToken: <csrf_token>
  - Content-Type: application/json

Note: The Superset port is dynamically assigned by Aspire at startup. To find the current port:

  1. Open the Aspire Dashboard
  2. Find the "superset" service
  3. Copy the endpoint URL

Dashboard 1: Data Platform Overview

Purpose: Executive view of total data volumes - impressive numbers!

New Datasets (SQL-based)

Dataset: platform_metrics_summary

SELECT
    'Passports' as entity, COUNT(*)::bigint as count, 'prism' as component FROM prism.passports
UNION ALL SELECT 'Identifiers', COUNT(*), 'prism' FROM prism.identifiers
UNION ALL SELECT 'Catalog Items', COUNT(*), 'catalog' FROM catalog.items
UNION ALL SELECT 'Item Types', COUNT(*), 'catalog' FROM catalog.item_types
UNION ALL SELECT 'Shipments', COUNT(*), 'transport' FROM transport.shipments
UNION ALL SELECT 'Deliveries', COUNT(*), 'transport' FROM transport.deliveries
UNION ALL SELECT 'Allocations', COUNT(*), 'transport' FROM transport.shipment_item_allocations
UNION ALL SELECT 'RFID Reads', COUNT(*), 'iot' FROM iot.zb_tag_reads
UNION ALL SELECT 'Camera Reads', COUNT(*), 'iot' FROM iot.dl_camera_reads
UNION ALL SELECT 'IoT Devices', COUNT(*), 'iot' FROM iot.devices

Dataset: item_type_distribution

SELECT
    it.name as item_type,
    COUNT(i.id) as item_count
FROM catalog.items i
JOIN catalog.item_types it ON it.id = i.item_type_id
GROUP BY it.name
ORDER BY item_count DESC

Charts for Dashboard 1

Chart Name Type Dataset Metric
Total Passports big_number_total platform_metrics count WHERE entity='Passports'
Total Identifiers big_number_total platform_metrics count WHERE entity='Identifiers'
Total Items big_number_total platform_metrics count WHERE entity='Catalog Items'
Data by Component pie platform_metrics SUM(count) GROUP BY component
Item Types Top 20 bar item_type_distribution item_count

Dashboard 2: Shipment Lifecycle Tracking (STAR DASHBOARD)

Purpose: Showcase the new camera→RFID→basket correlation feature

New Datasets

Dataset: lifecycle_metrics_summary

SELECT
    'Stacks Tracked' as metric,
    COUNT(DISTINCT matched_stack_id)::bigint as value
FROM bbu.camera_read_processing
WHERE processing_status = 'Matched'
UNION ALL
SELECT 'Shipments Tracked', COUNT(DISTINCT matched_shipment_id)
FROM bbu.camera_read_processing
WHERE matched_shipment_id IS NOT NULL
UNION ALL
SELECT 'Baskets Correlated', COUNT(*)
FROM bbu.camera_read_baskets
UNION ALL
SELECT 'Camera Reads Processed', COUNT(*)
FROM iot.dl_camera_reads
UNION ALL
SELECT 'RFID Reads Captured', COUNT(*)
FROM iot.zb_tag_reads

Dataset: camera_processing_status

SELECT
    processing_status,
    COUNT(*) as count,
    COUNT(DISTINCT matched_stack_id) as unique_stacks,
    COUNT(DISTINCT matched_shipment_id) as unique_shipments
FROM bbu.camera_read_processing
GROUP BY processing_status

Dataset: hourly_processing_trend

SELECT
    DATE_TRUNC('hour', dcr.event_timestamp) as hour,
    dcr.file_format,
    COUNT(*) as camera_reads,
    COUNT(CASE WHEN crp.processing_status = 'Matched' THEN 1 END) as matched,
    COUNT(CASE WHEN crp.processing_status = 'DeadLettered' THEN 1 END) as dead_lettered,
    ROUND(100.0 * COUNT(CASE WHEN crp.processing_status = 'Matched' THEN 1 END) / NULLIF(COUNT(*), 0), 1) as match_rate
FROM iot.dl_camera_reads dcr
LEFT JOIN bbu.camera_read_processing crp ON crp.dl_camera_read_id = dcr.id
WHERE dcr.event_timestamp >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY DATE_TRUNC('hour', dcr.event_timestamp), dcr.file_format
ORDER BY hour

Dataset: basket_correlation_detail

SELECT
    crp.id,
    dcr.decoded_value as lpn,
    dcr.event_timestamp as camera_time,
    crp.processing_status,
    crp.matched_stack_id,
    crp.matched_shipment_id,
    COUNT(crb.id) as basket_count
FROM bbu.camera_read_processing crp
JOIN iot.dl_camera_reads dcr ON dcr.id = crp.dl_camera_read_id
LEFT JOIN bbu.camera_read_baskets crb ON crb.camera_read_id = crp.dl_camera_read_id
WHERE crp.processing_status = 'Matched'
GROUP BY crp.id, dcr.decoded_value, dcr.event_timestamp, crp.processing_status, crp.matched_stack_id, crp.matched_shipment_id
ORDER BY dcr.event_timestamp DESC

Charts for Dashboard 2

Chart Name Type Dataset Description
Stacks Tracked big_number_total lifecycle_metrics The headline number
Baskets Correlated big_number_total lifecycle_metrics Shows IoT value
Match Rate big_number_total hourly_processing Today's rate
Processing Status pie camera_processing_status Matched vs DeadLettered
Match Rate Trend echarts_timeseries_line hourly_processing_trend Hour by hour
Format Comparison echarts_timeseries_bar hourly_processing_trend TXT vs XML success
Basket Distribution histogram basket_correlation_detail Baskets per stack
Recent Correlations table basket_correlation_detail Live feed view

Dashboard 3: Operations Dashboard

Purpose: Daily operational metrics for monitoring

Datasets (Use existing views)

  • bbu_oblpn_status_daily (existing, ID: 5)
  • bbu_oblpn_load_progress (existing, ID: 7)
  • bbu_oblpn_location_backlog (existing, ID: 8)

New Dataset: rfid_daily_trend

SELECT
    DATE(event_timestamp) as read_date,
    COUNT(*) as total_reads,
    COUNT(DISTINCT epc) as unique_tags,
    COUNT(DISTINCT device_id) as active_readers
FROM iot.zb_tag_reads
WHERE event_timestamp >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY DATE(event_timestamp)
ORDER BY read_date

Charts for Dashboard 3

Chart Name Type Dataset Description
Daily Throughput echarts_area status_daily Existing chart (ID: 4)
Load Progress pivot_table load_progress Existing chart (ID: 6)
Location Backlog bar location_backlog Existing chart (ID: 7)
RFID Daily Volume echarts_timeseries_bar rfid_daily_trend New
Active Readers big_number_total rfid_daily_trend Device count

Dashboard 4: Data Quality & Processing

Purpose: Engineering metrics for data quality monitoring

New Datasets

Dataset: dead_letter_analysis

SELECT
    processor_name,
    CASE
        WHEN last_error LIKE '%Camera decode did not yield%' THEN 'Empty Barcode'
        WHEN last_error LIKE '%No stack item%' THEN 'No Stack in Oracle'
        WHEN last_error LIKE '%basket%' OR last_error LIKE '%RFID%' THEN 'No RFID Match'
        ELSE 'Other'
    END as failure_reason,
    COUNT(*) as count,
    MAX(last_failure_utc) as last_occurrence
FROM bbu.processing_dead_letters
WHERE is_dead_lettered = true
GROUP BY processor_name,
    CASE
        WHEN last_error LIKE '%Camera decode did not yield%' THEN 'Empty Barcode'
        WHEN last_error LIKE '%No stack item%' THEN 'No Stack in Oracle'
        WHEN last_error LIKE '%basket%' OR last_error LIKE '%RFID%' THEN 'No RFID Match'
        ELSE 'Other'
    END

Dataset: format_comparison

SELECT
    file_format,
    COUNT(*) as total,
    COUNT(CASE WHEN crp.processing_status = 'Matched' THEN 1 END) as matched,
    ROUND(100.0 * COUNT(CASE WHEN crp.processing_status = 'Matched' THEN 1 END) / NULLIF(COUNT(*), 0), 1) as match_pct
FROM iot.dl_camera_reads dcr
LEFT JOIN bbu.camera_read_processing crp ON crp.dl_camera_read_id = dcr.id
WHERE dcr.event_timestamp >= CURRENT_DATE
GROUP BY file_format

Charts for Dashboard 4

Chart Name Type Dataset Description
Dead Letter Breakdown pie dead_letter_analysis By failure reason
Processing Failures bar dead_letter_analysis Count by reason
TXT vs XML Success bar format_comparison Format comparison
Dead Letter Trend timeseries dead_letters (with time) Failures over time

Implementation Steps

Step 1: Get Fresh Auth Tokens

# Get JWT
curl -X POST http://localhost:8088/api/v1/security/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin","provider":"db"}'

# Get CSRF
curl http://localhost:8088/api/v1/security/csrf_token/ \
  -H "Authorization: Bearer $TOKEN"

Step 2: Create SQL Datasets (Virtual Tables)

For each SQL dataset above:

curl -X POST http://localhost:8088/api/v1/dataset/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-CSRFToken: $CSRF" \
  -H "Content-Type: application/json" \
  -d '{
    "database": 1,
    "schema": "bbu",
    "table_name": "lifecycle_metrics_summary",
    "sql": "SELECT ... (the SQL query)",
    "owners": [1]
  }'

Step 3: Create Charts

For each chart:

curl -X POST http://localhost:8088/api/v1/chart/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-CSRFToken: $CSRF" \
  -H "Content-Type: application/json" \
  -d '{
    "slice_name": "Stacks Tracked",
    "viz_type": "big_number_total",
    "datasource_id": <dataset_id>,
    "datasource_type": "table",
    "params": "{...chart params...}",
    "owners": [1]
  }'

Step 4: Create Dashboards

curl -X POST http://localhost:8088/api/v1/dashboard/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-CSRFToken: $CSRF" \
  -H "Content-Type: application/json" \
  -d '{
    "dashboard_title": "BBU Shipment Lifecycle",
    "published": true,
    "owners": [1]
  }'

Step 5: Add Charts to Dashboard

Update dashboard with position_json containing chart layouts.


Execution Order

  1. Phase 1: Create all SQL datasets (6 new datasets)
  2. Phase 2: Create Dashboard 2 charts (the star dashboard)
  3. Phase 3: Create Dashboard 2 and add charts
  4. Phase 4: Create remaining dashboards and charts
  5. Phase 5: Review and iterate based on feedback

Success Metrics

After implementation:

  • 4 dashboards created
  • ~20 charts total
  • ~10 new datasets
  • All dashboards accessible at http://localhost:8088/dashboard/list/

Key Talking Points for Stakeholders

  1. Scale: "We're tracking 1.5M+ passports, 3.6M identifiers, 860K+ catalog items"
  2. Integration: "Real-time RFID and camera data correlation - 70K+ tag reads, 36K+ camera events"
  3. Visibility: "For the first time, we can see which baskets are on which stacks - 127 stacks with 1,997 basket correlations"
  4. Quality: "29% match rate on the new format vs 1% on the old - 26x improvement"
  5. Volume: "Processing ~18K RFID reads per day, correlated with camera events in real-time"