Importer¶
L'importer prend un profil YAML validé, lit le tableur associé, résout les fichiers sur disque, et crée ou met à jour les items et fichiers en base.
Pipeline¶
┌──────────┐ ┌──────────────┐ ┌────────────────┐ ┌─────────────┐
│ profil + │ → │ lecteur_ │ → │ transformateur │ → │ resolveur_ │
│ tableur │ │ tableur │ │ │ │ fichiers │
└──────────┘ └──────────────┘ └────────────────┘ └─────────────┘
│
▼
┌──────────────┐
│ ecrivain │
│ (transaction│
│ tout-ou- │
│ rien) │
└──────────────┘
Quatre modules découpés sous src/archives_tool/importers/ :
lecteur_tableur.py— lit un CSV/Excel avec pandas endtype=str, normalise strip + NFC, convertit les sentinelles devaleurs_nulleset lesNaNenNone.transformateur.py— fonction pure ligne →ItemPrepare. Applique les trois formes de mapping, lesvaleurs_par_defaut, la décomposition de cote par regex et la décomposition de type par séparateur.resolveur_fichiers.py— cherche les fichiers sur disque selonprofil.fichiers. Mode template ({champ}substitué, résultat utilisé en glob) ou regex (liste + filtre par cohérence des groupes nommés avec l'item).ecrivain.py— orchestre tout, écrit en base sous transaction, journalise dansOperationImport. Réutilise les services métier (creer_fonds,modifier_collection,creer_item) qui garantissent les invariants Fonds / Collection / Item.
Dry-run par défaut¶
La commande CLI tourne en dry-run par défaut : le profil est lu, les
lignes transformées, les fichiers résolus, les diffs calculés par
rapport à la base actuelle — puis session.rollback() en fin.
Rien n'est écrit en base, aucun hash SHA-256 n'est calculé (rapide).
Le rapport affiché montre ce qui serait fait en mode réel.
Pour exécuter pour de vrai :
En mode réel :
- les hash SHA-256 sont calculés pour chaque fichier ajouté ;
- un
batch_idUUID est généré ; - une entrée
OperationImportest créée (table journal dédiée) avec le rapport complet sérialisé en JSON ; - l'ensemble est dans une seule transaction : rollback complet si une seule ligne remonte une erreur. L'import est tout-ou-rien en mode réel.
Valeurs par défaut¶
valeurs_par_defaut dans le profil :
Sémantique :
- Copiées sur chaque item, une par une. Pas de résolution dynamique ni de référence partagée (principe d'autonomie des items).
- Ne remplacent pas une valeur déjà présente dans le tableur :
si le tableur a une colonne
langueavec"spa"sur une ligne, la valeur du tableur l'emporte. - Complètent les champs absents du mapping : un item sans colonne
etat_catalogagedans le tableur reçoit"brouillon".
Ré-import¶
Le ré-import se fait par (fonds_id, cote) (la cote est unique par
fonds, pas globalement) :
- Item existant avec mêmes valeurs → inchangé (compteur
items_inchanges). - Item existant avec une différence sur une colonne ou dans
metadonnees→ mis à jour (items_mis_a_jour).modifie_parest écrit aveccree_parcourant. - Item absent → créé (
items_crees), automatiquement rattaché à la miroir du fonds (invariant 6).
La comparaison des colonnes numériques est tolérante : pandas lit en
str, SQLite stocke en int pour les colonnes Integer, donc
"1960" et 1960 sont considérés équivalents. Sans cette tolérance,
chaque ré-import marquerait artificiellement à jour tous les items
avec un champ comme annee.
Granularité fichier¶
Si granularite_source: "fichier", chaque ligne du tableur décrit
un fichier, pas un item. L'importer regroupe les lignes par
cote avant l'écriture :
- Métadonnées item fusionnées : première valeur non-
Noneretenue, divergences entre lignes →warningsdans le rapport. - Fichiers concaténés dans l'ordre d'apparition.
La fixture cas_fichier_groupe illustre ce cas : 3 lignes pour 2
items (PF-001 avec 2 fichiers, PF-002 avec 1 fichier).
Rapport¶
L'objet RapportImport retourné par importer() contient :
| Champ | Type | Sens |
|---|---|---|
dry_run |
bool | Mode d'exécution. |
batch_id |
str | None | UUID de l'opération (None en dry-run). |
fonds_cote |
str | None | Cote du fonds importé. |
fonds_cree |
bool | True si c'est le premier import du fonds. |
miroir_personnalisee |
bool | True si le profil a override la miroir. |
items_crees |
int | Nouveaux items. |
items_mis_a_jour |
int | Items modifiés. |
items_inchanges |
int | Items déjà identiques. |
fichiers_ajoutes |
int | Nouveaux Fichier en base. |
fichiers_deja_connus |
int | Fichiers déjà référencés. |
fichiers_orphelins |
list[str] | Sur disque, pas référencés. |
lignes_ignorees |
list[tuple[int, str]] | (n° ligne, raison). |
warnings |
list[str] | Divergences non-bloquantes. |
erreurs |
list[str] | Erreurs bloquantes en mode réel. |
duree_secondes |
float | Temps total. |
Codes de sortie CLI :
0: succès (même en dry-run avec warnings).1: l'import a remonté deserreurs.2: config ou profil invalide (erreur amont, profil v1 obsolète).
Exemples¶
Les fixtures sous tests/fixtures/profils/ sont testées bout en bout
par tests/test_importer.py :
cas_item_simple/— le cas le plus courant.cas_fichier_groupe/— granularité fichier + DOI Nakala.cas_hierarchie_cote/—decomposition_cote+decomposition_type.cas_uri_dc/— colonnes URI Dublin Core + agrégations.
Erreurs fréquentes¶
« Racine logique inconnue »¶
La clé déclarée dans profil.fichiers.racine n'est pas dans
config.racines de la config locale. Vérifier que le config_local.yaml
du poste déclare bien la racine.
« Motif template référence un champ absent de l'item »¶
Le motif motif_chemin contient un placeholder {champ} qui n'a
pas de valeur correspondante sur l'item. Champs disponibles :
{cote}(toujours) ;- toutes les clés de
champs_colonne(colonnes dédiées) ; - toutes les clés de
metadonnees(préfixemetadonnees.retiré) ; - toutes les clés de
hierarchie(sidecomposition_cotea matché).
« Profil v1 obsolète »¶
Les profils écrits avant V0.9.0-gamma.1 (avec une section
collection: racine) ne sont plus acceptés. Voir
Profils d'import pour la migration v1 → v2.
Rollback en mode réel¶
Si une seule ligne remonte une erreur, l'import entier est annulé.
Lancer d'abord en dry-run pour identifier toutes les erreurs d'un
coup (qui elles sont toutes collectées sans interruption), corriger,
puis relancer en --no-dry-run.