diff --git a/archivist/config.jsonc b/archivist/config.jsonc new file mode 100644 index 0000000..d566e7e --- /dev/null +++ b/archivist/config.jsonc @@ -0,0 +1,156 @@ +{ + // Example configuration for the Archivist pipeline. + // Keys in English; extracted values and rendered file names are in + // French, as they appear in the source documents. + + // ========================================================================== + // fields: declaration of every field handled by the rules. + // A missing mandatory field at the end of the pipeline marks the document + // invalid and routes it to pending/ for human review. + // "template" is a regular field: rules select the naming template with it. + // ========================================================================== + "fields": [ + { "name": "emittingCompany", "type": "string", "mandatory": true }, + { "name": "documentType", "type": "string", "mandatory": true }, + { "name": "date", "type": "string", "mandatory": true }, + { "name": "template", "type": "string", "mandatory": false }, + { "name": "bankAccount", "type": "string", "mandatory": false }, + { "name": "address", "type": "string", "mandatory": false }, + { "name": "reference", "type": "string", "mandatory": false } + ], + + // ========================================================================== + // rules: an ordered tree evaluated against the first-page text + // (firstPageText) and the fields already set or deleted by earlier rules. + // + // Each rule contains four keys: + // when (required): expr-lang expression; the rest of the rule runs only + // if it evaluates to true + // set : list of assignments + // { "field": "x", "value": } constant + // { "field": "x", "eval": "" } evaluated in the + // same context + // delete: list of field names to unset + // rules : child rules, evaluated in order only if the parent "when" + // matched; nesting is recursive + // + // Built-in helper: firstMatch(regex) returns the first match of the regex + // in the first-page text (first capture group when present). + // ========================================================================== + "rules": [ + // --- Generic extraction, runs for every document ------------------------- + // Emission date, e.g. "Paris, le 15/01/2026" -> "15/01/2026" + { + "when": "true", + "set": [ + { "field": "date", "eval": "firstMatch("[0-9]{2}/[0-9]{2}/[0-9]{4}")" } + ] + }, + + // --- Bank statements (relevés de compte) ---------------------------------- + { + "when": "firstPageText contains "Relevé de compte"", + "set": [ + { "field": "documentType", "value": "Relevé de compte" }, + { "field": "template", "value": "bank_statement" } + ], + "rules": [ + // Issuing bank + { + "when": "firstPageText contains "BRED"", + "set": [ { "field": "emittingCompany", "value": "BRED" } ] + }, + { + "when": "firstPageText contains "Fortunéo"", + "set": [ { "field": "emittingCompany", "value": "Fortunéo" } ] + }, + { + "when": "firstPageText contains "Crédit Mutuel"", + "set": [ { "field": "emittingCompany", "value": "Crédit Mutuel" } ] + }, + // Personal accounts, matched on the account number printed on page 1 + { + "when": "firstPageText contains "12345678901"", + "set": [ { "field": "bankAccount", "value": "Compte Thomas" } ] + }, + { + "when": "firstPageText contains "98765432109"", + "set": [ { "field": "bankAccount", "value": "Compte joint" } ] + } + ] + }, + + // --- Taxe d'habitation, one child rule per address ------------------------ + { + "when": "firstPageText contains "Taxe d'habitation"", + "set": [ + { "field": "documentType", "value": "Taxe d'habitation" }, + { "field": "emittingCompany", "value": "DGFiP" }, + { "field": "template", "value": "tax_notice_housing" } + ], + "rules": [ + { + "when": "firstPageText contains "12 rue des Lilas"", + "set": [ { "field": "address", "value": "12 rue des Lilas, Paris" } ] + }, + { + "when": "firstPageText contains "34 avenue Jean Jaurès"", + "set": [ { "field": "address", "value": "34 avenue Jean Jaurès, Bordeaux" } ] + } + ] + }, + + // --- Avis d'impôt sur le revenu -------------------------------------------- + { + "when": "firstPageText contains "Impôt sur le revenu" && firstPageText contains "avis d'imposition"", + "set": [ + { "field": "documentType", "value": "Avis d'impôt sur le revenu" }, + { "field": "emittingCompany", "value": "DGFiP" }, + { "field": "template", "value": "tax_notice_income" } + ] + }, + + // --- Generic invoices (e-commerce and others) ----------------------------- + { + "when": "firstPageText contains "Facture" && firstPageText contains "TVA"", + "set": [ + { "field": "documentType", "value": "Facture" }, + { "field": "template", "value": "invoice" }, + // Invoice number, e.g. "Facture n° F-2026-0123" -> "F-2026-0123" + { "field": "reference", "eval": "firstMatch("[Ff]acture n° ?([A-Za-z0-9-]+)")" } + ], + // Fields that do not apply to invoices, in case an earlier rule + // set them + "delete": [ "bankAccount", "address" ], + "rules": [ + { + "when": "firstPageText contains "Amazon"", + "set": [ { "field": "emittingCompany", "value": "Amazon" } ] + }, + { + "when": "firstPageText contains "LDLC"", + "set": [ { "field": "emittingCompany", "value": "LDLC" } ] + }, + { + "when": "firstPageText contains "Cdiscount"", + "set": [ { "field": "emittingCompany", "value": "Cdiscount" } ] + } + ] + } + ], + + // ========================================================================== + // templates: named naming templates, selected by the "template" field. + // The rendered template is the full object path under files/. + // {date} is rendered as yyyy-mm-dd when set (normalized; "/" is not usable + // in a S3 key). A field that no rule has set renders as an empty string. + // If no rule sets "template", the "default" template is used. + // ========================================================================== + "templates": { + "bank_statement": "{emittingCompany}/{date}_{documentType}_{bankAccount}.pdf", + "tax_notice_housing": "Impots/{date}_{documentType}_{address}.pdf", + "tax_notice_income": "Impots/{date}_{documentType}.pdf", + "invoice": "Factures/{date}_{emittingCompany}_{reference}.pdf", + "default": "Divers/{documentType}.pdf" + } +}