From bc642ce0a8d6e0f7eb8812475e66db03366126b5 Mon Sep 17 00:00:00 2001 From: Fred Boniface Date: Thu, 10 Jul 2025 00:17:34 +0100 Subject: [PATCH] Release initial version supporting report-generation. Currently only through CLI. --- src/formHandler.ts | 4 +- src/models/report.ts | 2 +- src/reports/reportGen.ts | 84 ------------------------------------- src/reports/reportHtml.ts | 20 ++++++--- src/reports/reports.ts | 22 ++++++++++ static/generate-report.html | 28 +++++++++++++ 6 files changed, 68 insertions(+), 92 deletions(-) delete mode 100644 src/reports/reportGen.ts create mode 100644 static/generate-report.html diff --git a/src/formHandler.ts b/src/formHandler.ts index 831ce96..b692cb0 100644 --- a/src/formHandler.ts +++ b/src/formHandler.ts @@ -11,7 +11,7 @@ export interface Report { unitNumber: string; reported: string; comments: string; - utcTimestamp: string; + utcTimestamp: Date; faults: Fault[]; } @@ -27,7 +27,7 @@ export async function handleFormData(data: any) { unitNumber: data.unitNumber, reported: data.reported, comments: data.comments || '', - utcTimestamp: data.utcTimestamp, + utcTimestamp: new Date(data.utcTimestamp), faults: [] }; diff --git a/src/models/report.ts b/src/models/report.ts index bde3a2e..c5a94cc 100644 --- a/src/models/report.ts +++ b/src/models/report.ts @@ -12,7 +12,7 @@ const ReportSchema = new Schema({ unitNumber: { type: String, required: true }, reported: { type: String, required: true }, comments: { type: String, required: false }, - utcTimestamp: { type: String, required: true }, + utcTimestamp: { type: Date, required: true }, faults: { type: [FaultSchema], required: true }, }); diff --git a/src/reports/reportGen.ts b/src/reports/reportGen.ts deleted file mode 100644 index e28f36b..0000000 --- a/src/reports/reportGen.ts +++ /dev/null @@ -1,84 +0,0 @@ -interface ReportSummary { - totalReports: number; - firstTimestamp: Date; - lastTimestamp: Date; - reportsPerUnit: Record; -} - -interface vehicleDetails { - vehicleNumber: string; - reportCount: number; - cabFront: boolean; - cabRear: boolean; -} - -interface ReportsByUnit { - unitNumber: string; - vehicles: vehicleDetails[]; - comments: string[]; -} - -interface Report { - unitNumber: string; - reported: string; - comments: string; - utcTimestamp: string; - faults: string[]; -} - -export async function organiseReports(): Promise { - try { - const res = await fetch('https://rep.fjla.uk/fetch'); - if (!res.ok) throw new Error(`Fetch failed: ${res.statusText}`); - - const reports: Report[] = await res.json(); - - if (reports.length === 0) { - return { - totalReports: 0, - firstTimestamp: new Date(0), // Epoch - lastTimestamp: new Date(0), - reportsPerUnit: {}, - }; - } - - // Sort by string utcTimestamp (lexical sort works fine for ISO strings) - reports.sort((a, b) => a.utcTimestamp.localeCompare(b.utcTimestamp)); - - const firstTimestamp = reports[0].utcTimestamp; - const lastTimestamp = reports[reports.length - 1].utcTimestamp; - const totalReports = reports.length; - - const reportsPerUnit: Record = {}; - for (const report of reports) { - const unit = report.unitNumber; - reportsPerUnit[unit] = (reportsPerUnit[unit] || 0) + 1; - } - - return { - totalReports, - firstTimestamp: new Date(firstTimestamp), - lastTimestamp: new Date(lastTimestamp), - reportsPerUnit, - }; - } catch (err) { - console.error('Error organising reports:', err); - throw err; - } -} - -// Fetch all unit numbers. -// Group reports by unit number. -// Build HTML for each report. - -(async () => { - try { - const summary = await organiseReports(); - console.log("Total reports:", summary.totalReports); - console.log("First report:", summary.firstTimestamp.toISOString()); - console.log("Last report:", summary.lastTimestamp.toISOString()); - console.log("Reports per unit:", summary.reportsPerUnit); - } catch (err) { - console.error("Failed to organise reports:", err); - } -})(); \ No newline at end of file diff --git a/src/reports/reportHtml.ts b/src/reports/reportHtml.ts index 42d5bed..90e4e0c 100644 --- a/src/reports/reportHtml.ts +++ b/src/reports/reportHtml.ts @@ -1,6 +1,6 @@ import { GroupedUnitReport, VehicleReport } from "./reports"; -export function generateHTMLReport(units: GroupedUnitReport[], startDate: Date, endDate: Date): string { +export function generateHTMLReport(units: GroupedUnitReport[], startDate: Date, endDate: Date, totalReports: number): string { const unitsWithReports = units.filter(unit => unit.vehicles.some( v => v.saloonReports > 0 || v.cabReports > 0 @@ -8,14 +8,14 @@ export function generateHTMLReport(units: GroupedUnitReport[], startDate: Date, ); const body = unitsWithReports.map(renderUnitSection).join('\n'); - return wrapHtml(body, startDate, endDate); + return wrapHtml(body, startDate, endDate, totalReports); } function renderUnitSection(unit: GroupedUnitReport): string { const total = unit.vehicles.length; return ` -
+

${unit.unitNumber}

${unit.vehicles.map((v, i) => renderCoach(v, i, total)).join('\n')} @@ -57,7 +57,7 @@ function renderComments(comments: string[]): string { `; } -function wrapHtml(content: string, startDate: Date, endDate: Date): string { +function wrapHtml(content: string, startDate: Date, endDate: Date, totalReports: number): string { const formatDate = (d: Date) => d.toISOString().split('T')[0]; return ` @@ -70,6 +70,11 @@ function wrapHtml(content: string, startDate: Date, endDate: Date): string { font-family: sans-serif; } + .avoid-break { + page-break-inside: avoid; + break-inside: avoid; + } + .preface { text-align: center; font-size: 0.9em; @@ -152,13 +157,18 @@ function wrapHtml(content: string, startDate: Date, endDate: Date): string { .comments-box p { margin: 0.5em 0; } + + .bold { + font-weight: bold; + }

TrACreport

Report period: ${formatDate(startDate)} - ${formatDate(endDate)}

-

Total reports received for each vehicle in the West fleet

+

Total reports received: ${totalReports}

+

Reports are shown per saloon or cab

158 cabs are not recorded as cab cooling is disabled for Guards

${content} diff --git a/src/reports/reports.ts b/src/reports/reports.ts index 053b24b..5dede8a 100644 --- a/src/reports/reports.ts +++ b/src/reports/reports.ts @@ -1,3 +1,6 @@ +import { generateHTMLReport } from "./reportHtml.js"; +import { writeFile } from "fs/promises"; + interface BaseReport { unitNumber: string; reported: string; @@ -94,6 +97,25 @@ function populateFaultReports( return grouped; } + +(async () => { + console.log('Fetching unit layout...'); + const unitLayout = await fetchUnitLayout(); + console.log('Unit layout:', unitLayout); + console.log('Fetching reports...') + const reports = await fetchReports(); + console.log('Reports:', reports) + const totalReports = reports.length; + + const grouped = buildInitialReportStructure(unitLayout) + const finalReport = populateFaultReports(grouped, reports) + + const htmlReport = generateHTMLReport(finalReport, new Date("2025-06-27"), new Date("2025-07-09"), totalReports) + await writeFile('out.html', htmlReport, 'utf-8'); +})().catch(err => { + console.error('Unhandled error:', err); + process.exit(1); +}); /* USAGE: const unitLayout = await fetchUnitLayout(); const reports = await fetchReports(); diff --git a/static/generate-report.html b/static/generate-report.html new file mode 100644 index 0000000..55dbace --- /dev/null +++ b/static/generate-report.html @@ -0,0 +1,28 @@ + + + + + + Generate Report + + + +

Generate A/C Fault Report

+

This page is under development. For now, reports will be sent out through other means.

+ + +