/home/techb158/cosmic.abdallabala.com/src/services
Edit: /home/techb158/cosmic.abdallabala.com/src/services/reportingService.js (18670B)
const { DashboardService } = require("./dashboardService.js");
const { GateWorkflowService } = require("./gateWorkflowService.js");
const { IntegrationService } = require("./integrationService.js");
function nowIso() {
return new Date().toISOString();
}
function safe(value) {
return value == null ? "" : value;
}
function csvEscape(value) {
const text = String(safe(value));
if (/[",\n\r]/.test(text)) {
return `"${text.replace(/"/g, '""')}"`;
}
return text;
}
function toCsv(headers, rows) {
const headerLine = headers.map(header => csvEscape(header.label)).join(",");
const body = rows.map(row => headers.map(header => csvEscape(row[header.key])).join(",")).join("\n");
return `${headerLine}\n${body}${body ? "\n" : ""}`;
}
function htmlEscape(value) {
return String(safe(value))
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function percentage(value) {
const num = Number(value);
return Number.isFinite(num) ? `${Math.round(num)}%` : "Missing";
}
function average(values) {
const nums = values.map(Number).filter(Number.isFinite);
if (!nums.length) return 0;
return Math.round(nums.reduce((sum, value) => sum + value, 0) / nums.length);
}
function countBy(rows, key) {
return rows.reduce((result, row) => {
const value = row[key] || "Unspecified";
result[value] = (result[value] || 0) + 1;
return result;
}, {});
}
function sortedTop(rows, key, limit) {
return rows.slice().sort((a, b) => Number(b[key] || 0) - Number(a[key] || 0)).slice(0, limit || 10);
}
class ReportingService {
constructor(database) {
this.database = database;
this.dashboardService = new DashboardService(database);
this.gateWorkflowService = new GateWorkflowService(database);
this.integrationService = new IntegrationService(database);
}
getDashboardOrThrow(projectId) {
const result = this.dashboardService.getDashboard(projectId);
if (!result) throw new Error(`Project not found: ${projectId}`);
return result;
}
getExecutiveReport(projectId) {
const { data, dashboard } = this.getDashboardOrThrow(projectId);
const gateHistory = this.gateWorkflowService.listGateHistory(projectId);
const integrations = this.integrationService.listIntegrations(projectId);
const mappings = this.integrationService.listMappings(projectId);
const syncRuns = this.integrationService.listSyncRuns(projectId);
const openMitigations = (dashboard.mitigations || []).filter(item => item.status !== "Done" && item.status !== "Rejected");
const overdueMitigations = openMitigations.filter(item => {
if (!item.dueDate) return false;
const due = new Date(`${item.dueDate}T23:59:59`);
return !Number.isNaN(due.getTime()) && due < new Date();
});
const selectedExperiment = (dashboard.experiments || []).find(item => item.selected) || dashboard.experiments[0] || null;
return {
reportType: "Executive COSMIC AI-Risk report",
generatedAt: nowIso(),
project: dashboard.project,
sourceBasis: data.sourceBasis || {},
summary: dashboard.summary,
dimensions: dashboard.dimensions,
lifecycle: dashboard.lifecycle,
topRisks: sortedTop(dashboard.scoredRisks || [], "residualScore", 10),
mitigationSummary: {
total: (dashboard.mitigations || []).length,
open: openMitigations.length,
done: (dashboard.mitigations || []).filter(item => item.status === "Done").length,
averageProgress: average((dashboard.mitigations || []).map(item => item.progress_percent)),
averageEffectiveness: average((dashboard.mitigations || []).map(item => item.effectiveness_percent)),
overdue: overdueMitigations.length
},
gate: dashboard.gate,
latestPersistedGate: gateHistory[0] || null,
selectedExperiment,
integrationSummary: {
supportedApplications: integrations.map(item => item.provider),
configuredIntegrations: integrations.length,
connectedIntegrations: integrations.filter(item => item.connectionStatus === "Connected").length,
mappings: mappings.length,
syncRuns: syncRuns.length
},
sourceTraceability: [
{
element: "Governance dimensions",
sourceStatus: "Source-derived",
implementation: "Organizational, technical, and human risk panels."
},
{
element: "Measurement indicators",
sourceStatus: "Source-derived",
implementation: "Indicator catalog with measurand, unit, threshold, and interpretation rule."
},
{
element: "REST API and risk engine",
sourceStatus: "Source-derived objective",
implementation: "Dashboard, API endpoints, scoring service, and export service."
},
{
element: "Scoring and gate rules",
sourceStatus: "Software design extension",
implementation: "Probability, impact, detectability, mitigation progress, approval state, and review criteria."
}
]
};
}
getRiskRegisterReport(projectId) {
const { dashboard } = this.getDashboardOrThrow(projectId);
return {
reportType: "Risk register report",
generatedAt: nowIso(),
project: dashboard.project,
summary: {
totalRisks: dashboard.summary.totalRisks,
openRisks: dashboard.summary.openRisks,
highRisks: dashboard.summary.highRisks,
criticalRisks: dashboard.summary.criticalRisks,
distributionByDimension: countBy(dashboard.scoredRisks || [], "dimension"),
distributionByStatus: countBy(dashboard.scoredRisks || [], "status")
},
risks: dashboard.scoredRisks || []
};
}
getRiskRegisterCsv(projectId) {
const report = this.getRiskRegisterReport(projectId);
const headers = [
{ key: "id", label: "Risk ID" },
{ key: "title", label: "Risk title" },
{ key: "dimension", label: "Dimension" },
{ key: "domain", label: "Domain" },
{ key: "lifecyclePhase", label: "Lifecycle phase" },
{ key: "probability", label: "Probability" },
{ key: "impact", label: "Impact" },
{ key: "detectability", label: "Detectability" },
{ key: "rawScore", label: "Raw score" },
{ key: "normalizedScore", label: "Normalized score" },
{ key: "residualScore", label: "Residual score" },
{ key: "residualSeverity", label: "Residual severity" },
{ key: "status", label: "Status" },
{ key: "approvalStatus", label: "Approval status" },
{ key: "owner", label: "Owner" },
{ key: "dueDate", label: "Due date" },
{ key: "mitigationProgress", label: "Mitigation progress" },
{ key: "mitigationEffectiveness", label: "Mitigation effectiveness" },
{ key: "evidence", label: "Evidence summary" }
];
return toCsv(headers, report.risks);
}
getMitigationReport(projectId) {
const { dashboard } = this.getDashboardOrThrow(projectId);
const mitigations = dashboard.mitigations || [];
return {
reportType: "Mitigation report",
generatedAt: nowIso(),
project: dashboard.project,
summary: {
total: mitigations.length,
done: mitigations.filter(item => item.status === "Done").length,
inProgress: mitigations.filter(item => item.status === "In progress").length,
notStarted: mitigations.filter(item => item.status === "Not started").length,
averageProgress: average(mitigations.map(item => item.progress_percent)),
averageEffectiveness: average(mitigations.map(item => item.effectiveness_percent))
},
mitigations
};
}
getMitigationCsv(projectId) {
const report = this.getMitigationReport(projectId);
const headers = [
{ key: "id", label: "Mitigation ID" },
{ key: "riskId", label: "Risk ID" },
{ key: "riskTitle", label: "Linked risk" },
{ key: "riskDimension", label: "Risk dimension" },
{ key: "title", label: "Mitigation title" },
{ key: "description", label: "Description" },
{ key: "status", label: "Status" },
{ key: "progress_percent", label: "Progress percent" },
{ key: "effectiveness_percent", label: "Effectiveness percent" },
{ key: "dueDate", label: "Due date" },
{ key: "owner", label: "Owner" },
{ key: "evidenceCount", label: "Evidence count" },
{ key: "riskResidualScore", label: "Risk residual score" },
{ key: "riskResidualSeverity", label: "Risk residual severity" }
];
return toCsv(headers, report.mitigations);
}
getGateReport(projectId) {
const { dashboard } = this.getDashboardOrThrow(projectId);
const gateHistory = this.gateWorkflowService.listGateHistory(projectId);
return {
reportType: "Deployment gate report",
generatedAt: nowIso(),
project: dashboard.project,
currentGate: dashboard.gate,
persistedGateHistory: gateHistory,
latestPersistedGate: gateHistory[0] || null
};
}
getIndicatorReport(projectId) {
const { dashboard } = this.getDashboardOrThrow(projectId);
return {
reportType: "Indicator report",
generatedAt: nowIso(),
project: dashboard.project,
indicators: dashboard.indicators || []
};
}
getIntegrationReport(projectId) {
const { dashboard } = this.getDashboardOrThrow(projectId);
return {
reportType: "Project-management integration report",
generatedAt: nowIso(),
project: dashboard.project,
integrations: this.integrationService.listIntegrations(projectId),
mappings: this.integrationService.listMappings(projectId),
syncRuns: this.integrationService.listSyncRuns(projectId)
};
}
getAuditReport(projectId) {
const { dashboard } = this.getDashboardOrThrow(projectId);
return {
reportType: "Audit trail report",
generatedAt: nowIso(),
project: dashboard.project,
auditEvents: this.gateWorkflowService.listAuditEvents(projectId, 500)
};
}
getFullReport(projectId) {
return {
reportType: "Full COSMIC AI-Risk evidence package",
generatedAt: nowIso(),
executive: this.getExecutiveReport(projectId),
riskRegister: this.getRiskRegisterReport(projectId),
mitigations: this.getMitigationReport(projectId),
gate: this.getGateReport(projectId),
indicators: this.getIndicatorReport(projectId),
integrations: this.getIntegrationReport(projectId),
audit: this.getAuditReport(projectId)
};
}
renderExecutiveHtml(projectId) {
const report = this.getExecutiveReport(projectId);
const topRisks = report.topRisks.map(risk => `
| ${htmlEscape(risk.id)} |
${htmlEscape(risk.title)} |
${htmlEscape(risk.dimension)} |
${htmlEscape(risk.lifecyclePhase)} |
${htmlEscape(risk.residualScore)}/100 |
${htmlEscape(risk.residualSeverity)} |
${htmlEscape(risk.owner)} |
`).join("");
const criteriaRows = (report.gate.criteria || []).map(item => `
| ${htmlEscape(item.name)} |
${htmlEscape(item.required)} |
${htmlEscape(item.actual)} |
${htmlEscape(item.status)} |
${htmlEscape(item.evidence)} |
`).join("");
const lifecycleRows = (report.lifecycle || []).map(phase => `
| ${htmlEscape(phase.id)} |
${htmlEscape(phase.name)} |
${htmlEscape(phase.readiness)}/100 |
${htmlEscape(phase.openRisks)} |
${htmlEscape(phase.riskScore)}/100 |
${htmlEscape(phase.riskLevel)} |
`).join("");
const indicatorRows = this.getIndicatorReport(projectId).indicators.map(indicator => `
| ${htmlEscape(indicator.id)} |
${htmlEscape(indicator.name)} |
${htmlEscape(indicator.measurand)} |
${htmlEscape(indicator.unit)} |
${htmlEscape(indicator.interpretation)} |
`).join("");
return `
${htmlEscape(report.project.name)} Executive Report
COSMIC AI-Risk Management
${htmlEscape(report.project.name)}
${htmlEscape(report.project.subtitle || "Integrated Measurement Framework for AI Project Risks")}
Report type: ${htmlEscape(report.reportType)}
Generated: ${htmlEscape(report.generatedAt)}
Project type: ${htmlEscape(report.project.projectType)}
Lifecycle phase: ${htmlEscape(report.project.lifecyclePhase)}
Overall AI risk score${htmlEscape(report.summary.overallScore)}/100${htmlEscape(report.summary.riskLevel)}
Deployment gate${htmlEscape(report.summary.gateStatus)}${htmlEscape(report.gate.message)}
Open risks${htmlEscape(report.summary.openRisks)}${htmlEscape(report.summary.totalRisks)} total risks
Mitigation completion${htmlEscape(report.summary.mitigationCompletion)}%${htmlEscape(report.mitigationSummary.done)} mitigations done
Governance triangle scores
Organizational ${htmlEscape(report.dimensions.Organizational || 0)}/100
Technical ${htmlEscape(report.dimensions.Technical || 0)}/100
Human ${htmlEscape(report.dimensions.Human || 0)}/100
Mitigation status
Total actions: ${htmlEscape(report.mitigationSummary.total)}
Open actions: ${htmlEscape(report.mitigationSummary.open)}
Average progress: ${htmlEscape(percentage(report.mitigationSummary.averageProgress))}
Average effectiveness: ${htmlEscape(percentage(report.mitigationSummary.averageEffectiveness))}
Lifecycle readiness and risk exposure
| ID | Lifecycle phase | Readiness | Open risks | Risk score | Risk level |
${lifecycleRows}
Top residual risks
| ID | Risk | Dimension | Lifecycle phase | Residual | Level | Owner |
${topRisks}
Deployment gate criteria
| Criterion | Required | Actual | Status | Evidence |
${criteriaRows}
Indicator catalog
| ID | Indicator | Measurand | Unit | Interpretation |
${indicatorRows}
Project-management integration summary
Supported applications: ${htmlEscape(report.integrationSummary.supportedApplications.join(", "))}
Configured integrations: ${htmlEscape(report.integrationSummary.configuredIntegrations)}
External mappings: ${htmlEscape(report.integrationSummary.mappings)}
Sync runs: ${htmlEscape(report.integrationSummary.syncRuns)}
Source traceability
| Element | Source status | Implementation |
${report.sourceTraceability.map(row => `| ${htmlEscape(row.element)} | ${htmlEscape(row.sourceStatus)} | ${htmlEscape(row.implementation)} |
`).join("")}
`;
}
}
module.exports = {
ReportingService,
toCsv,
csvEscape
};