Data Quality Reports
Summary
The data quality system detects anomalies in a partner’s active lot and programme data via lib/data-quality.ts. Admins can view a live summary, preview an HTML report, export a CSV, and create a persisted QualityReport record. Reports are editable while in draft status and can be sent to the partner’s contact email via Resend (with up to 3 retry attempts). Once sent, a report is immutable.
State Diagram
stateDiagram-v2 state "Vérification en direct" as LiveCheck state "Rapport brouillon" as Draft state "Rapport envoyé" as Sent [*] --> LiveCheck: admin views summary, preview or CSV LiveCheck --> [*]: ephemeral — no DB record [*] --> Draft: admin creates report draft Draft --> Draft: admin edits report Draft --> Sent: admin sends report Sent --> [*]: immutable
Steps
1. Compute Anomalies (Actor: system)
computePartnerAnomalies(partnerId) from lib/data-quality.ts queries all active lots for the partner and identifies:
- Lots missing price (and not
priceMissing: true) - Lots with price errors
- Lots with
prixSurDemande - Lots missing surface
- Lots missing
numeroLot - Programmes without photos
- Programmes without an address
Returns { summary: PartnerAnomaliesSummary, details: AnomalyDetail[], programmesWithoutPhotos: string[], programmesWithoutAddress: string[], lotsPriceSurDemande: ..., ... }.
2. Live Quality Summary (Actor: admin)
GET /admin/partners/:id/quality-summary — returns the summary object only (no detail rows). Fast overview for the admin dashboard.
Outcome: { data: summary } with counts per anomaly type
3. HTML Preview (Actor: admin)
GET /admin/partners/:id/quality-reports/preview — renders a full HTML report via renderReportHtml() and returns it as text/html. Not persisted. Allows admin to review the email content before creating a draft.
Outcome: Full HTML email preview in browser
4. CSV Export (Actor: admin)
GET /admin/partners/:id/quality-reports/csv — serializes anomalies to RFC 4180 CSV with UTF-8 BOM. Filename pattern: quality-report-{partner-name}-{date}.csv. Streamed as a file download.
Outcome: CSV file with all anomaly details
5. Create Report Draft (Actor: admin)
POST /admin/partners/:id/quality-reports:
- Runs
computePartnerAnomalies(). - Returns 400
NO_ANOMALIESif there are no anomalies to report (nothing to send). - Creates
QualityReportwithstatus: "draft",subject,body(HTML),summary,details.
Outcome: QualityReport record in DB with status draft
6. List Report History (Actor: admin)
GET /admin/partners/:id/quality-reports — returns all quality reports for the partner, ordered newest first. body field excluded from list for performance.
7. View Single Report (Actor: admin)
GET /admin/quality-reports/:reportId — full report including body HTML.
8. Edit Draft Report (Actor: admin)
PUT /admin/quality-reports/:reportId with { subject?, body? }:
- Returns 409
ALREADY_SENTif the report has been sent. bodyis sanitized viasanitizeHtml()(strips<script>tags andon*attributes).- Updates
subjectand/orbodyin place.
Outcome: Draft report updated
9. Send Report via Resend (Actor: admin, tools: resend-email)
POST /admin/quality-reports/:reportId/send:
- Returns 409
ALREADY_SENTif already dispatched. - Returns 400
NO_CONTACT_EMAILif partner has no email on file. - Sanitizes
bodyHTML before sending. - Calls Resend API (
from: "data-quality@sealion.cacio.fr") with up to 3 attempts (delays: 1s, 3s between retries). - On success:
status → "sent",sentAt,sentToset on the report. - On all attempts failed: 502
EMAIL_SEND_FAILED.
Outcome: Email delivered, report marked sent
Error States
- Partner not found → 404
PARTNER_NOT_FOUND - Report not found → 404
REPORT_NOT_FOUND - Creating report with no anomalies → 400
NO_ANOMALIES - Editing or sending an already-sent report → 409
ALREADY_SENT - Sending when partner has no contact email → 400
NO_CONTACT_EMAIL - Resend API failure (all retries exhausted) → 502
EMAIL_SEND_FAILED
Related Processes
- partner-mapping-activation — partner must be active with ingested data before quality reports are meaningful
- sftp-xml-ingestion — post-ingestion data quality check runs automatically and posts to Slack