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;
|
||||
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: []
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@ const ReportSchema = new Schema<ReportDocument>({
|
||||
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 },
|
||||
});
|
||||
|
||||
|
@ -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";
|
||||
|
||||
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 `
|
||||
<div class="train-container">
|
||||
<div class="train-container avoid-break">
|
||||
<h1 class="unit-number">${unit.unitNumber}</h1>
|
||||
<div class="vehicle-container">
|
||||
${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;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="preface">
|
||||
<h1>TrACreport</h1>
|
||||
<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>
|
||||
</div>
|
||||
${content}
|
||||
|
@ -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();
|
||||
|
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