Executive Reporting Experience

EPR Tool Employee Performance Reporting Tool

EPR Tool is a corporate reporting platform handcrafted and tailored for Octopus Digital's reporting requirements. It converts operational Jira records into leadership-ready planning, performance, and delivery intelligence.

Octopus Digital Reporting Framework

How EPR Tool transforms delivery data into decision-ready insights

The model below clarifies what starts in Jira, what EPR orchestrates, and what business outcomes the reports enable.

Jira Input

Source signals
  • Roadmap inventoryList of roadmap items with planning baselines and current work status.
  • Phase planningPhases of roadmap items with phase-level planning context.
  • Daily activity logsResource activities mapped with planned effort and actual hour logs.
  • Leave recordsEmployee leave records that influence execution availability.
  • Components & tagsComponents and categorization of roadmap items for portfolio slicing.

EPR Enrichment

Orchestrated intelligence
  • Roadmap-level hoursHours logged per roadmap item, derived from underlying subtask execution.
  • Capacity & availabilityEmployee capacity and true availability after leave and calendar constraints.
  • Ramadan & holiday calendarCalendar-aware context to normalize delivery expectations.
  • Idle vs overloadIdle-time and overloading detection to balance utilization decisions.
  • Roadmap diagnosticsRoot-cause visibility on why an epic got late.
  • Pre-failure indicatorsEarly signals to detect epic delivery breakdown risk before timeline breach.
  • Plan versioningRoadmap planning continuity and version trail of commitment changes.
  • Budget intelligenceApproved budget vs budget revisions over time vs actual spent analysis.

EPR Output / Insights

Leadership questions answered
  • Where are we slipping?Which roadmap items are on-track, at-risk, or slipping - and which phase is causing the delay?
  • Are we balancing talent?Where are we underutilizing people, and where are critical teams overloaded?
  • Calendar vs planning?How much delivery variance comes from leaves, Ramadan/holidays, or planning quality?
  • Is the spend rational?Which epics are financially inefficient because budget changes outpace delivery progress?
  • What should we approve now?Re-sequencing, reallocation, descoping, or timeline reset - which is the right call?
  • Are we learning?How has each roadmap commitment evolved across versions - improving or repeating planning errors?
  • Who are our top performers?Which employees consistently deliver on time with high productivity and minimal rework or penalty load?
  • Productivity & utilization?How does each employee's logged effort compare against capacity, planned hours, and team benchmarks?
  • Discipline & missed entries?Who has data-quality gaps such as missed worklog entries, late updates, or incomplete time tracking?
  • Penalty exposure?Which contributors carry the highest weighted penalty from delays, overruns, bugs, or unplanned leaves?
  • Team accountability mix?How is performance distributed across teams - where is concentration risk, and where is delivery resilient?
EPR Tool creates a single narrative from raw Jira activity to business insight so planning, execution, and governance teams can act on the same source of truth.
Source Jira Records
Orchestration EPR Intelligence
Outcome Leadership Actions
Technical blueprint

Detailed software architecture

EPR Tool is structured as a layered reporting system with clearly separated functional modules. Each module exists to solve a specific reporting problem, passes forward a cleaner and richer form of delivery truth, and allows leadership to move from raw operational activity to auditable decisions without needing to inspect the codebase.

1

Operational intake module

This module captures the raw operational picture: Jira work hierarchy, worklogs, planning fields, leave context, and controlled leadership inputs. Its purpose is to ensure no report starts from assumptions or partial extracts. Everything downstream depends on this intake layer to preserve traceability back to the operating record.

2

Trust and standardization module

Raw delivery data is not decision-ready, so this module reconciles naming, dates, hierarchy links, and planning structures into one shared shape. It exists to eliminate report-to-report drift and make every later calculation compare like with like.

3

Business context and interpretation module

Here the platform adds meaning, not just structure. Capacity rules, leave impact, holiday effects, delivery-risk signals, budget movement, and timeline interpretation are layered onto the standardized data so leadership can understand why performance changed, not only that it changed.

4

Canonical reporting memory

This module keeps a durable reporting-grade version of the truth that multiple pages can reuse. It exists so dashboards, performance views, and governance screens all read from the same historical and current state instead of recalculating incompatible answers independently.

5

Insight composition module

Once the reporting memory is stable, this module shapes it into leadership products: scorecards, detailed reports, utilization views, risk narratives, and planning comparisons. Its role is to translate shared data into audience-specific decision surfaces without changing the underlying truth.

6

Experience and action module

The final module delivers the reports through a consistent local experience with shared navigation, interactive pages, and live report APIs. It exists so executives, PMO, and delivery leaders can consume one connected reporting estate, move across views, and act on the same evidence with minimal friction.

System architecture diagram

The diagram shows how the functional architecture modules connect to move from operating records to leadership action.

Use the zoom controls to enlarge the architecture map without losing the full end-to-end context.

110%
flowchart LR subgraph Sources["Operational source domain"] Jira["Jira delivery records
work hierarchy, status, worklogs"] Plans["Controlled planning inputs
roadmaps, budgets, governance data"] Calendars["Availability context
leaves, calendars, working patterns"] end Intake["Operational intake module
collects and preserves source evidence"] Trust["Trust and standardization module
aligns hierarchy, ownership, dates, structures"] Context["Business context and interpretation module
adds capacity, delay, budget, and risk meaning"] Memory["Canonical reporting memory
stores reusable reporting-grade truth"] Compose["Insight composition module
builds dashboards, scorecards, narratives"] Experience["Experience and action module
delivers reports, filters, APIs, navigation"] Decisions["Leadership and PMO actions
governance, recovery, prioritization decisions"] Jira --> Intake Plans --> Intake Calendars --> Intake Intake --> Trust --> Context --> Memory --> Compose --> Experience --> Decisions Memory --> Experience

Full-width Mermaid rendering keeps the architecture legible while still showing how the platform’s modules cooperate as one reporting system.

Operational path

Detailed data-flow architecture

The data path is intentionally staged: raw Jira facts are acquired, canonicalized, enriched with business context, persisted for reuse, then rendered into HTML reports and live endpoints. That split keeps reporting consistent while allowing multiple dashboards to reuse the same transformed facts.

  1. 1. Capture operational signals Jira issues, subtasks, worklogs, leaves, budget fields, and planner references are collected from APIs and managed exports.
  2. 2. Standardize identifiers and dates Extraction logic aligns epic-story-subtask relationships, date semantics, assignee naming, and project mappings into stable report inputs.
  3. 3. Apply business enrichment Capacity, availability, holiday impacts, phase structure, delay heuristics, and budget comparisons are derived as reusable facts.
  4. 4. Persist canonical reporting state SQLite databases hold refreshed facts so report generation and API responses can reuse the same validated source of truth.
  5. 5. Generate report-ready outputs HTML generators assemble metrics, tables, charts, and narratives for each leadership-facing page in the reporting suite.
  6. 6. Serve and consume The local report server exposes reports and APIs, and the browser applies shared navigation plus page-specific scripts for final interaction.

Data flow diagram

This view traces the full movement from Jira extraction to user decisions on localhost.

flowchart TD Raw["1. Raw operational inputs
Jira APIs, workbooks, planner files"] Extract["2. Extraction and collection
Python fetch/export scripts"] Canon["3. Canonical shaping
hierarchy joins, dates, assignee alignment"] Enriched["4. Business enrichment
capacity, leave, risk, budget, diagnostics"] Persist["5. Persisted reporting state
SQLite stores and caches"] Compose["6. Report composition
generate_*.py pipelines and templates"] Serve["7. Local delivery
report_server.py routes and APIs"] Act["8. User consumption
reports, filters, governance actions"] Admin["Admin and planning metadata
managed fields, categories, planner settings"] Raw --> Extract --> Canon --> Enriched --> Persist --> Compose --> Serve --> Act Admin --> Canon Admin --> Enriched Persist --> Serve

This flow emphasizes the separation between acquisition, canonical state, report generation, and final decision consumption.

Visual module map

Architecture diagrams

Four focused diagrams that together cover every module, database, and report in the platform. Each diagram zooms into a different cross-section of the architecture so no single view becomes too cluttered to read.

Diagram 1 of 4

Full module layer map

All 65 modules organised into their architectural layer — from Jira data acquisition through canonical shaping, report generation, and Flask serving to the 23 HTML reports consumed by leadership.

Scroll horizontally inside the diagram panel to see all layers end-to-end.

130%
flowchart LR subgraph SRC["Source Domain"] J["Jira REST API
boards · issues · worklogs · leaves"] P["Planning Inputs
budgets · roadmaps · governance"] end subgraph L1["Layer 1 — Jira Export and Integration"] jc["jira_client.py"] ji["jira_incremental_cache.py"] je["jira_export_db.py"] e1["export_jira_work_items.py"] e2["export_jira_nested_view.py"] e3["export_jira_subtask_worklogs.py"] e4["export_jira_subtask_worklog_rollup.py"] e5["export_ipp_phase_breakdown.py"] fd["fetch_jira_dashboard.py"] end subgraph L2["Layer 2 — Canonical and Registries"] cr["canonical_report_data.py"] mp["managed_projects_registry.py"] mf["manage_fields_registry.py"] re["report_entity_registry.py"] iu["ipp_meeting_utils.py"] end subgraph L3["Layer 3 — Report Generators (18)"] g1["generate_assignee_hours_report.py"] g2["generate_employee_performance_report.py"] g3["generate_gantt_chart_html.py"] g4["generate_ipp_meeting_dashboard.py"] g5["generate_leaves_planned_calendar_html.py"] g6["generate_missed_entries_html.py"] g7["generate_nested_view_html.py"] g8["generate_original_estimates_hierarchy_report.py"] g9["generate_phase_rmi_gantt_html.py"] g10["generate_planned_actual_table_view.py"] g11["generate_planned_rmis_html.py"] g12["generate_planned_vs_dispensed_report.py"] g13["generate_rlt_leave_report.py"] g14["generate_rmi_jira_gantt_html.py"] g15["generate_rnd_data_story.py"] g16["monthly_epic_plan_progress_service.py"] g17["delayed_epic_chain_gantt_service.py"] g18["planned_actual_table_view_service.py"] end subgraph L4["Layer 4 — Server and API"] rs["report_server.py
Flask · all routes · sync"] db["dashboard_db_enrichment.py"] of["offline_html_prepare.py"] sg["sync_team_rmi_gantt_sqlite.py"] ws["wsgi.py"] ii["init_epics_management_db.py"] end subgraph L5["Layer 5 — HTML Reports (23)"] R["23 HTML reports
report_html/ directory"] end subgraph AST["Shared Assets"] A1["shared-nav.js"] A2["shared-date-filter.js"] A3["shared-nav.css"] A4["material-symbols.css"] end SRC --> L1 --> L2 --> L3 --> L4 --> L5 L2 -.->|registries and config| L4 AST --> L5
Diagram 2 of 4

Database and state connections

Which modules read from and write to each of the three SQLite databases that hold the platform's persistent state.

flowchart TD subgraph DB1["jira_sync_cache.db
Jira API cache"] D1[("jira_sync_cache.db")] end subgraph DB2["assignee_hours_capacity.db
Capacity and utilization"] D2[("assignee_hours_capacity.db")] end subgraph DB3["epics_management.db
Planner and IPP settings"] D3[("epics_management.db")] end jic["jira_incremental_cache.py"] jed["jira_export_db.py"] exp["export_jira_*.py scripts"] crd["canonical_report_data.py"] gah["generate_assignee_hours_report.py"] rs1["report_server.py"] init["init_epics_management_db.py"] dbe["dashboard_db_enrichment.py"] rs2["report_server.py"] jic -->|write delta cache| D1 jed -->|write export cache| D1 D1 -->|read raw data| exp D1 -->|read for shaping| crd gah -->|read and write| D2 D2 -->|read capacity| rs1 init -->|schema init| D3 dbe -->|read and write| D3 D3 -->|read planner data| rs2
Diagram 4 of 4

Server and serving model

How canonical source files flow through sync_report_html() into the Flask server, then to the browser and Azure App Service.

flowchart TD subgraph canonical["Canonical Sources — repo root"] ch["Root .html files
dashboard_template.html
introduction.html"] gen["generate_*.py output
18 HTML generators"] sh["shared-nav.js
shared-date-filter.js
shared-nav.css
material-symbols.css"] end sync["sync_report_html()
runs on every server start
copies canonical to served"] subgraph served["report_html/ — served directory"] rh["Synced .html copies
Shared asset copies"] end flask["report_server.py
Flask app · port 3000-3003"] subgraph apis["REST API endpoints"] a1["/api/canonical-refresh"] a2["/api/epics-management/rows"] a3["/api/projects"] end browser["Browser
23 reports · nav · date filter"] azure["Azure App Service
epreporting.azurewebsites.net"] canonical --> sync --> served --> flask flask --> apis flask --> browser flask -.->|wsgi.py| azure
Diagram 3 of 4

Report generation pipeline — all 18 generators to their HTML outputs

Every generator module mapped to its output report, grouped by business domain. Some generators share a service layer; one report receives input from two generators.

Scroll horizontally to follow the generator-to-report connections across all four domains.

120%
flowchart LR subgraph PERF["Performance and People"] pg1["generate_assignee_hours_report.py"] --> pr1(["assignee_hours_report.html"]) pg2["generate_employee_performance_report.py"] --> pr2(["employee_performance_report.html"]) pg3["generate_missed_entries_html.py"] --> pr3(["missed_entries.html"]) pg4["generate_rlt_leave_report.py"] --> pr4(["rlt_leave_report.html"]) pg5["generate_leaves_planned_calendar_html.py"] --> pr5(["leaves_planned_calendar.html"]) end subgraph DELIV["Delivery and Planning"] dg1["generate_gantt_chart_html.py"] --> dr1(["gantt_chart_report.html"]) dg2["generate_nested_view_html.py"] --> dr2(["nested_view_report.html"]) dg3["generate_ipp_meeting_dashboard.py"] --> dr3(["ipp_meeting_dashboard.html"]) dg4["monthly_epic_plan_progress_service.py"] --> dr4(["monthly_epic_plan_progress_report.html"]) dg5["delayed_epic_chain_gantt_service.py"] --> dr5(["delayed_epic_chain_gantt_report.html"]) end subgraph FIN["Financial and Estimates"] fg1["generate_planned_vs_dispensed_report.py"] --> fr1(["planned_vs_dispensed_report.html"]) fg2["generate_original_estimates_hierarchy_report.py"] --> fr2(["original_estimates_hierarchy_report.html"]) fg3["generate_planned_actual_table_view.py"] --> fr3(["planned_actual_table_view.html"]) fg4["planned_actual_table_view_service.py"] --> fr3 end subgraph RMI["RMI and Gantt"] rg1["generate_rmi_jira_gantt_html.py"] --> rr1(["rmi_jira_gantt_report.html"]) rg2["generate_phase_rmi_gantt_html.py"] --> rr2(["phase_rmi_gantt_report.html"]) rg3["generate_planned_rmis_html.py"] --> rr3(["planned_rmis_report.html"]) rg4["generate_rnd_data_story.py"] --> rr4(["rnd_data_story.html"]) end
Codebase map

Module inventory — 65 active modules

Every functional unit in the platform, organised by architectural layer. Use this as a quick-reference map to understand what each file does and which layer it belongs to — from Jira data acquisition all the way to the HTML reports consumed by leadership.

65Active modules
23HTML reports
18Report generators
56Test files
3SQLite databases
1

Jira Export & Integration

9 modules
jira_client.py
Core Jira REST API client — issues, boards, worklogs, subtasks
jira_incremental_cache.py
Incremental sync — only fetches records changed since last run
jira_export_db.py
SQLite persistence layer for Jira export cache
export_jira_work_items.py
Full hierarchy export: epics → stories → subtasks
export_jira_nested_view.py
Nested view structure export for hierarchical reports
export_jira_subtask_worklogs.py
Subtask-level worklog extraction per assignee per date
export_jira_subtask_worklog_rollup.py
Aggregates subtask worklogs up to story/epic level
export_ipp_phase_breakdown.py
Phase breakdown export for IPP meeting dashboard
fetch_jira_dashboard.py
Fetches Jira dashboard config, project metadata, board settings
2

Canonical Data & Registries

5 modules
canonical_report_data.py
Single source of truth — normalizes and validates all report metrics
managed_projects_registry.py
Registry of active projects with metadata, colors, and config
manage_fields_registry.py
Custom Jira field mapping management; maps field IDs to human names
report_entity_registry.py
Registry of all report pages, categories, nav labels, and routes
ipp_meeting_utils.py
Shared utilities for IPP meeting dashboard — date math, phase aggregation
3

Report Generators

18 modules
generate_assignee_hours_report.py
Capacity vs logged hours per person; leave-adjusted utilization
generate_employee_performance_report.py
Scorecard: productivity, penalty, discipline, utilization, resignations
generate_gantt_chart_html.py
Interactive Gantt timeline from nested view; swimlanes by project
generate_ipp_meeting_dashboard.py
Phase-level planning vs actuals; meeting-ready delivery status
generate_leaves_planned_calendar_html.py
Leave calendar with planned vs actual; team availability heat map
generate_missed_entries_html.py
Worklog data-quality gaps; missing entries by person and date
generate_nested_view_html.py
Epic → story → subtask hierarchy with hours, status, and scoring
generate_original_estimates_hierarchy_report.py
Original vs revised vs actual estimates by epic and project
generate_phase_rmi_gantt_html.py
Phase-gated Gantt; shows RMI timing per delivery phase
generate_planned_actual_table_view.py
Planned vs actual hours table with variance columns
generate_planned_rmis_html.py
Resource management items planned per period with status
generate_planned_vs_dispensed_report.py
Approved budget vs actual spend; financial variance per epic
generate_rlt_leave_report.py
Resource leave tracking calendar; team availability view
generate_rmi_jira_gantt_html.py
Integrated RMI + Jira Gantt with phase and dependency mapping
generate_rnd_data_story.py
R&D narrative; investment split and delivery confidence
monthly_epic_plan_progress_service.py
Month-over-month epic plan progress; commitment vs delivery trend
delayed_epic_chain_gantt_service.py
Gantt of epics with delay chains; cascading impact visualization
planned_actual_table_view_service.py
Service layer backing the planned vs actual table API and report
4

Server & API

5 modules
report_server.py
Main Flask app — all routes, sync_report_html(), REST APIs, static serving
dashboard_db_enrichment.py
Enriches dashboard data from epics_management.db; applies IPP fields and planner meta
offline_html_prepare.py
Bundles reports into offline-distributable HTML packages
sync_team_rmi_gantt_sqlite.py
Syncs team RMI Gantt data from Jira into SQLite for fast serving
wsgi.py
WSGI entry point for Azure App Service deployment
5

HTML Reports

23 reports
homeintroduction.html — landing page and architecture overview
dashboarddashboard.html — executive KPI dashboard with epic cards
bar_chartexecutive_dashboard.html — high-level executive summary
groupsassignee_hours_report.html — capacity vs logged hours per person
workspace_premiumemployee_performance_report.html — scorecard: productivity, penalty, discipline
view_timelinegantt_chart_report.html — interactive project timeline
event_noteipp_meeting_dashboard.html — IPP meeting planner and phase status
beach_accessleaves_planned_calendar.html — team leave calendar and availability
event_busymissed_entries.html — worklog data-quality gaps
account_treenested_view_report.html — epic → story → subtask hierarchy
straightenoriginal_estimates_hierarchy_report.html — original vs revised vs actual
stacked_bar_chartphase_rmi_gantt_report.html — phase-gated RMI Gantt
compare_arrowsplanned_actual_table_view.html — planned vs actual hours table
list_altplanned_rmis_report.html — planned resource management items
savingsplanned_vs_dispensed_report.html — budget vs actual spend variance
calendar_monthrlt_leave_report.html — resource leave tracking calendar
timelinermi_jira_gantt_report.html — RMI + Jira integrated Gantt
sciencernd_data_story.html — R&D investment narrative
approvalapproved_vs_planned_hours_report.html — approved vs planned hours
warningdelayed_epic_chain_gantt_report.html — cascading delay chain view
trending_upmonthly_epic_plan_progress_report.html — month-over-month epic progress
peopleteam_capacity_planner.html — team capacity planning tool
webdashboard_template.html — executive dashboard generation template
6

Shared Assets

4 modules
shared-nav.js
Navigation bar injected into every report; active-state detection; mobile menu
shared-date-filter.js
Global date range filter component; persists selection across reports
shared-nav.css
Navigation bar and base layout styles shared across all reports
material-symbols.css
Google Material Symbols icon font — self-hosted for offline use