Release initial version supporting report-generation. Currently only through CLI.
This commit is contained in:
parent
84530f7a1f
commit
bc642ce0a8
@ -11,7 +11,7 @@ export interface Report {
|
|||||||
unitNumber: string;
|
unitNumber: string;
|
||||||
reported: string;
|
reported: string;
|
||||||
comments: string;
|
comments: string;
|
||||||
utcTimestamp: string;
|
utcTimestamp: Date;
|
||||||
faults: Fault[];
|
faults: Fault[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export async function handleFormData(data: any) {
|
|||||||
unitNumber: data.unitNumber,
|
unitNumber: data.unitNumber,
|
||||||
reported: data.reported,
|
reported: data.reported,
|
||||||
comments: data.comments || '',
|
comments: data.comments || '',
|
||||||
utcTimestamp: data.utcTimestamp,
|
utcTimestamp: new Date(data.utcTimestamp),
|
||||||
faults: []
|
faults: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ const ReportSchema = new Schema<ReportDocument>({
|
|||||||
unitNumber: { type: String, required: true },
|
unitNumber: { type: String, required: true },
|
||||||
reported: { type: String, required: true },
|
reported: { type: String, required: true },
|
||||||
comments: { type: String, required: false },
|
comments: { type: String, required: false },
|
||||||
utcTimestamp: { type: String, required: true },
|
utcTimestamp: { type: Date, required: true },
|
||||||
faults: { type: [FaultSchema], required: true },
|
faults: { type: [FaultSchema], required: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
interface ReportSummary {
|
|
||||||
totalReports: number;
|
|
||||||
firstTimestamp: Date;
|
|
||||||
lastTimestamp: Date;
|
|
||||||
reportsPerUnit: Record<string, number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ReportSummary> {
|
|
||||||
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<string, number> = {};
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,6 +1,6 @@
|
|||||||
import { GroupedUnitReport, VehicleReport } from "./reports";
|
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 =>
|
const unitsWithReports = units.filter(unit =>
|
||||||
unit.vehicles.some(
|
unit.vehicles.some(
|
||||||
v => v.saloonReports > 0 || v.cabReports > 0
|
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');
|
const body = unitsWithReports.map(renderUnitSection).join('\n');
|
||||||
return wrapHtml(body, startDate, endDate);
|
return wrapHtml(body, startDate, endDate, totalReports);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderUnitSection(unit: GroupedUnitReport): string {
|
function renderUnitSection(unit: GroupedUnitReport): string {
|
||||||
const total = unit.vehicles.length;
|
const total = unit.vehicles.length;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="train-container">
|
<div class="train-container avoid-break">
|
||||||
<h1 class="unit-number">${unit.unitNumber}</h1>
|
<h1 class="unit-number">${unit.unitNumber}</h1>
|
||||||
<div class="vehicle-container">
|
<div class="vehicle-container">
|
||||||
${unit.vehicles.map((v, i) => renderCoach(v, i, total)).join('\n')}
|
${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];
|
const formatDate = (d: Date) => d.toISOString().split('T')[0];
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@ -70,6 +70,11 @@ function wrapHtml(content: string, startDate: Date, endDate: Date): string {
|
|||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avoid-break {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
.preface {
|
.preface {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
@ -152,13 +157,18 @@ function wrapHtml(content: string, startDate: Date, endDate: Date): string {
|
|||||||
.comments-box p {
|
.comments-box p {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="preface">
|
<div class="preface">
|
||||||
<h1>TrACreport</h1>
|
<h1>TrACreport</h1>
|
||||||
<h2>Report period: ${formatDate(startDate)} - ${formatDate(endDate)}</h2>
|
<h2>Report period: ${formatDate(startDate)} - ${formatDate(endDate)}</h2>
|
||||||
<p>Total reports received for each vehicle in the West fleet</p>
|
<p class="bold">Total reports received: ${totalReports}</p>
|
||||||
|
<p>Reports are shown per saloon or cab</p>
|
||||||
<p>158 cabs are not recorded as cab cooling is disabled for Guards</p>
|
<p>158 cabs are not recorded as cab cooling is disabled for Guards</p>
|
||||||
</div>
|
</div>
|
||||||
${content}
|
${content}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { generateHTMLReport } from "./reportHtml.js";
|
||||||
|
import { writeFile } from "fs/promises";
|
||||||
|
|
||||||
interface BaseReport {
|
interface BaseReport {
|
||||||
unitNumber: string;
|
unitNumber: string;
|
||||||
reported: string;
|
reported: string;
|
||||||
@ -94,6 +97,25 @@ function populateFaultReports(
|
|||||||
return grouped;
|
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:
|
/* USAGE:
|
||||||
const unitLayout = await fetchUnitLayout();
|
const unitLayout = await fetchUnitLayout();
|
||||||
const reports = await fetchReports();
|
const reports = await fetchReports();
|
||||||
|
28
static/generate-report.html
Normal file
28
static/generate-report.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>Generate Report</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Generate A/C Fault Report</h1>
|
||||||
|
<p>This page is under development. For now, reports will be sent out through other means.</p>
|
||||||
|
<!--
|
||||||
|
<form action="/report/generate" method="GET">
|
||||||
|
<div>
|
||||||
|
<label for="start">Start Date</label>
|
||||||
|
<input type="date" id="start" name="start" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="end">End Date</label>
|
||||||
|
<input type="date" id="end" name="end" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit">Generate Report</button>
|
||||||
|
</form>
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user