/home/techb158/cosmic.abdallabala.com/src/services
NameSizeModeActions
dashboardService.js21770666editdlrm
gateWorkflowService.js72960666editdlrm
integrationService.js274610666editdlrm
oauthService.js92820666editdlrm
reportingService.js186700666editdlrm
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

${lifecycleRows}
IDLifecycle phaseReadinessOpen risksRisk scoreRisk level

Top residual risks

${topRisks}
IDRiskDimensionLifecycle phaseResidualLevelOwner

Deployment gate criteria

${criteriaRows}
CriterionRequiredActualStatusEvidence

Indicator catalog

${indicatorRows}
IDIndicatorMeasurandUnitInterpretation

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

${report.sourceTraceability.map(row => ``).join("")}
ElementSource statusImplementation
${htmlEscape(row.element)}${htmlEscape(row.sourceStatus)}${htmlEscape(row.implementation)}
`; } } module.exports = { ReportingService, toCsv, csvEscape };