description: "Task list for feature implementation"
Tasks: Importation de photos depuis une carte mémoire
Input: Design documents from /specs/001-import-photos/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md
Tests: Incluses — plan.md § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération.
Organisation: Tasks groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune.
Dépendances inter-specs (à lire avant de commencer)
Ce module est le premier point d'intégration réel de trois autres specs déjà planifiées. Aucun code n'existe encore dans ce dépôt à ce jour.
| Dépendance | Fournit | État |
|---|---|---|
specs/004-categorisation-dossiers/tasks.md |
Squelette du monorepo (packages/regine-core, packages/regine-cli, packages/regine-gui, packages/regine-agent), regine_core.dossier.root.determine_root, regine_core.config.categories, regine_core.config.db |
tasks.md existe (27 tâches) — à exécuter avant la Phase 1 ci-dessous si ce n'est pas déjà fait |
specs/002-profil-boitiers-optionnel |
regine_core.camera_profile.resolve_collision, regine_core.metadata.exif (lecture Model/BodySerialNumber) |
Pas encore de tasks.md — à générer (/speckit-tasks sur 002) et implémenter avant la Phase 5 (US3, désambiguïsation de boîtiers) ; les Phases 1 à 4 de ce fichier n'en dépendent pas |
specs/005-checkout-reconciliation |
regine_core.archive.checkout.checkout |
Pas encore de tasks.md — nécessaire uniquement pour T034 (fusion vers un dossier déjà archivé) ; tout le reste de ce fichier n'en dépend pas |
Phase 1: Setup
Purpose: Vérifier les prérequis inter-specs et poser le squelette propre à ce module.
- T001 Vérifier que
packages/regine-core/src/regine_core/dossier/root.pyetpackages/regine-core/src/regine_core/config/categories.pyexistent déjà (créés parspecs/004-categorisation-dossiers/tasks.md) ; si absents, exécuter d'abord ce fichier de tâches avant de continuer - T002 [P] Créer
packages/regine-core/src/regine_core/import_carte/__init__.py - T003 [P] Créer
packages/regine-cli/src/regine_cli/import_cmd.py(squelette de la commanderegine import, sans logique) — reportée à la fin de US1 (T019), une fois la logique métier disponible à orchestrer - T004 [P] Créer les dossiers
packages/regine-core/tests/unit/etpackages/regine-core/tests/integration/s'ils n'existent pas déjà (normalement déjà créés par specs/004) — déjà présents
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Infrastructure partagée requise par les trois user stories de cette fonctionnalité.
⚠️ CRITICAL: Aucune user story ne peut être implémentée avant la fin de cette phase.
- T005 Créer
packages/regine-core/src/regine_core/import_carte/types.py: dataclassesFichierCandidat,GroupeImport,DestinationChoisie,Renommage(cf.data-model.md) - T006 Étendre
packages/regine-core/src/regine_core/metadata/exif.py(existant, specs/002) avecread_capture_date(chemin) -> datetime | None(tag EXIFDateTimeOriginal, cf.research.md§ 2) - T007 Étendre
packages/regine-core/src/regine_core/metadata/exif.pyavecwrite_document_id(chemin, identifiant) -> None(écritxmpMM:DocumentIDvia exiftool, idempotent, cf.research.md§ 3) —read_document_idajoutée aussi, utile pour tests/manifeste - T008 [P] Test unitaire de
read_capture_date/write_document_iddanspackages/regine-core/tests/unit/test_identifiant.py(date valide, date absente ; écriture puis relecture de l'identifiant) — validé avec de vraies opérations exiftool
Checkpoint: Fondations prêtes — les phases User Story peuvent commencer.
Phase 3: User Story 1 - Import simple d'une carte mémoire vers un nouveau dossier (Priority: P1) 🎯 MVP
Goal: Copier une carte, proposer un groupe unique, choisir titre et placement par défaut (année), renommer, attribuer un identifiant pérenne, confirmer et archiver (FR-001/002/004/005/007 [nouveau_dossier]/010/012/013/014/017/018/019).
Independent Test: Importer une carte d'une seule journée sans catégorie ; vérifier qu'un dossier correctement nommé et vérifié apparaît dans l'archive, sans dépendre d'aucune autre user story.
Tests for User Story 1
- T009 [P] [US1] Test unitaire copie vérifiée : une seule lecture de la carte, échec de vérification signalé sans marquer la carte sûre à effacer, dans
packages/regine-core/tests/unit/test_copie_checksum.py - T010 [P] [US1] Test unitaire découpage en groupe unique (cas simple, une seule journée) dans
packages/regine-core/tests/unit/test_groupage_dates.py - T011 [P] [US1] Test unitaire construction du nom de dossier et détection de collision dans
packages/regine-core/tests/unit/test_nommage.py - T012 [US1] Test d'intégration du pipeline complet (carte simple → dossier archivé, résumé confirmé) dans
packages/regine-core/tests/integration/test_pipeline_import_simple.py
Implementation for User Story 1
- T013 [US1] Implémenter
copier_carte(carte, local_tmp) -> list[FichierCandidat]danspackages/regine-core/src/regine_core/import_carte/copie.py(FR-001 : une seule lecture carte, FR-004 : filtrage des fichiers déjà importés) - T014 [US1] Implémenter
decouper_en_groupes(fichiers) -> list[GroupeImport]danspackages/regine-core/src/regine_core/import_carte/groupage.py(FR-002 lecture date, branche groupe unique par défaut de FR-005 ; le détachement multi-jours est complété en Phase 4) - T015 [US1] Implémenter la branche
nouveau_dossierderesoudre_destinationdanspackages/regine-core/src/regine_core/import_carte/destination.py(FR-007 pour ce cas, appelleregine_core.dossier.root.determine_root) - T016 [US1] Implémenter
construire_nom_dossier/renommer_fichiersdanspackages/regine-core/src/regine_core/import_carte/nommage.py(FR-010/012/013/014) - T017 [US1] Implémenter
attribuer_identifiantsdanspackages/regine-core/src/regine_core/import_carte/identifiant.py(FR-017, appellewrite_document_id) - T018 [US1] Implémenter
preparer_resume/archiverdanspackages/regine-core/src/regine_core/import_carte/push.py(FR-018/019) — complété le 2026-09-19 parpublier_copie_locale, suite à un écart avec FR-010 signalé par l'utilisateur après relecture du README (aucune copie de travail locale exploitable n'était laissée après import) ; réutilise le manifeste/verrou de specs/005 plutôt qu'un mécanisme parallèle — cf.contracts/regine-core-api.md - T019 [US1] Orchestrer le pipeline dans
packages/regine-cli/src/regine_cli/import_cmd.pypourregine import <carte> --annee --titre TEXTE(casnouveau_dossieruniquement) — validé en conditions réelles (python -m regine_cli.import_cmd import ...)
Checkpoint: User Story 1 fonctionnelle et testable indépendamment (MVP).
Phase 4: User Story 2 - Découpage d'un import multi-jours en plusieurs groupes (Priority: P2)
Goal: Détecter la plage de dates, exclure les dates aberrantes, mettre en avant un jour candidat au détachement, permettre à l'utilisateur de détacher et de traiter chaque groupe séparément (FR-003/005/006).
Independent Test: Importer une carte couvrant une semaine avec un pic isolé ; vérifier que ce jour est proposé comme candidat, jamais détaché automatiquement, et que le détacher produit deux dossiers distincts.
Tests for User Story 2
- T020 [P] [US2] Test unitaire d'exclusion des dates aberrantes du calcul de plage dans
packages/regine-core/tests/unit/test_groupage_dates.py - T021 [P] [US2] Test unitaire de mise en avant d'un jour candidat au détachement (heuristique, cf.
research.md§ 5) et de détachement manuel danstest_groupage_dates.py - T022 [US2] Test d'intégration du découpage en plusieurs groupes avec destination/titre distincts dans
packages/regine-core/tests/integration/test_pipeline_multi_jours.py
Implementation for User Story 2
- T023 [US2] Étendre
decouper_en_groupes: exclusion des dates aberrantes (FR-003) et heuristique de mise en avant d'un jour candidat (FR-006, cf.research.md§ 5), dansgroupage.py(dépend de T014) — implémentée dès T014 (Phase 3), une seule passe d'écriture cohérente - T024 [US2] Ajouter le détachement manuel d'un ou plusieurs jours par l'utilisateur, produisant des
GroupeImportdistincts, dansgroupage.py— implémentée dès T014 - T025 [US2] Étendre
packages/regine-cli/src/regine_cli/import_cmd.pypour afficher la répartition jour par jour et gérer le détachement interactif, puis demander destination/titre pour chaque groupe résultant — implémentée dès T019 (_proposer_detachement)
Checkpoint: User Stories 1 ET 2 fonctionnelles indépendamment.
Phase 5: User Story 3 - Import s'intégrant à un voyage en plusieurs étapes (Priority: P3)
Goal: Structure à deux niveaux (parent/sous-dossier), fusion locale et vers l'archive, désambiguïsation de boîtiers en cas de collision (FR-007 [nouveau_sous_dossier/nouveau_parent/fusion]/008/009/015/016).
Independent Test: Importer une première carte comme "nouveau dossier parent avec sa première étape", une seconde comme "nouveau sous-dossier", vérifier la structure à deux niveaux et l'héritage du répertoire racine.
Tests for User Story 3
- T026 [P] [US3] Test unitaire de recherche de dossiers candidats par titre/date proche (
difflib, cf.research.md§ 4) danspackages/regine-core/tests/unit/test_destination_candidats.py - T027 [P] [US3] Test unitaire : la branche
nouveau_sous_dossierréutilise leRootLocationdu parent sans nouvel appel àdetermine_root, danstest_destination_candidats.py - T028 [US3] Test d'intégration voyage complet (parent + sous-dossier + fusion locale + désambiguïsation de boîtiers) dans
packages/regine-core/tests/integration/test_pipeline_voyage.py - T029 [US3] Test d'intégration de la fusion vers un dossier déjà archivé dans
test_pipeline_voyage.py— teste un vrai appel àregine_core.archive.checkout.checkout(specs/005)
Implementation for User Story 3
- T030 [US3] Étendre
resoudre_destination: branchesnouveau_sous_dossier(hérite leRootLocationdu parent, FR-006 de specs/004) etnouveau_parent(FR-007), dansdestination.py(dépend de T015) — implémentées dès T015 - T031 [US3] Implémenter
lister_dossiers_candidats(titre_partiel, date_proche)dansdestination.py(FR-008, cf.research.md§ 4) — implémentée dès T015 - T032 [US3] Implémenter la branche
fusionlocale deresoudre_destination(dossier cible présent dans l'espace de travail local) dansdestination.py(FR-009, partie locale) —resoudre_fusion() - T033 [US3] Implémenter la branche
fusionvers un dossier déjà archivé : appelleregine_core.archive.checkout.checkout(specs/005-checkout-reconciliation), dansdestination.py—resoudre_fusion(), validé avec un vrai checkout - T034 [US3] Intégrer la désambiguïsation de boîtiers dans
copie.py: regrouper les fichiers en collision de nom d'origine, appelerregine_core.camera_profile.resolve_collision(specs/002), gérer les groupes nécessitant un étiquetage manuel (FR-015/016) —regrouper_par_nom_origine/resoudre_collisions_boitiers; copie anti-collision ajoutée danscopier_carte(ne jamais écraser un fichier de même nom d'origine) - T035 [US3] Étendre
packages/regine-cli/src/regine_cli/import_cmd.pypour les 4 types de destination et la résolution interactive de collision de boîtiers —--destination nouveau|parent|sous-dossier:CHEMIN|fusion:CHEMIN(cf.contracts/cli-import.md), étiquetage manuel interactif vialist_boitiers/assign_manual_source. Validation manuelle (CLI réel, pas seulement tests unitaires) a révélé et corrigé 2 bugs : (1) le sous-dossier d'étape n'était pas physiquement imbriqué sous le dossier parent réel, seulement sous la racine catégorie héritée ; (2)push.archiverécrasait silencieusement un fichier archivé de contenu différent en cas de collision de nom entre deux imports séparés — cf.CollisionNomArchiveErroretcontracts/regine-core-api.md
Checkpoint: User Stories 1, 2 et 3 fonctionnelles (la fusion vers un dossier déjà archivé reste conditionnée à specs/005, tout le reste est indépendant).
Phase 6: Polish & Cross-Cutting Concerns
- T036 [P] Exécuter manuellement les 3 scénarios de
specs/001-import-photos/quickstart.mdet consigner le résultat — exécutés via la vraie CLI (uv run python -m regine_cli.import_cmd import ..., entrées interactives réelles pour le détachement) : scénario 1 (import simple) OK, scénario 2 (détachement du jour au pic isolé, réponse "o") OK, scénario 3 (parent + sous-dossier imbriqué + fusion avec checkout automatique + désambiguïsation de boîtiers) OK - T037 [P]
ruff check --fixsurpackages/regine-coreetpackages/regine-cli - T038 Mettre à jour
contracts/cli-import.mdetcontracts/regine-core-api.mdsi l'implémentation révèle un écart avec les signatures documentées — les deux contrats réécrits pour refléter les signatures réelles (resoudre_destination/resoudre_fusion/regrouper_par_nom_origine/resoudre_collisions_boitiers, syntaxe--destination), et pour documenterCollisionNomArchiveError
Phase 7: Révision 2026-09-22 — archivage différé et classement par format
Contexte : deux écarts trouvés par test manuel de l'onglet d'import GUI (specs/006-gui-tri-consultation User Story 3) — cf. Clarifications de spec.md. (1) L'import poussait automatiquement vers l'archive NAS en fin d'opération, sans laisser au photographe l'occasion de relire/retoucher sa copie locale d'abord, et empêchait de tester l'import sans accès à un NAS réel. (2) Les fichiers n'étaient jamais classés dans un dossier de format (raw//jpeg//tiff/), contrairement à docs/archivage-photo-elements-cles.md section 10 et à l'écran de tri (specs/006) qui suppose cette structure déjà en place.
- T039 [P] Déplacer
dossier_de_format(et les ensembles d'extensions RAW/TIFF/JPEG) deregine_core.dossier.selectionversregine_core.import_carte.copie, ré-exportée depuisdossier.selectionpour ne pas casser les appelants existants (specs/006) — évite une dépendance inversée entre les deux modules (Principe VI) - T040 Créer
regine_core.import_carte.classement.classer_par_format(fichiers, racine)(FR-020) : déplace chaque fichier maître dans son dossier de format (déduit de sa propre extension, jamais celui d'un autre maître de même nom de base — cas RAW+JPEG jumeau), et chaque sidecar dans le dossier du maître RAW partageant son nom de base s'il y en a un, sinon celui de n'importe quel autre maître. Testé danspackages/regine-core/tests/unit/test_classement.py(5/5 tests, dont le cas jumeau RAW+JPEG+sidecar) - T041 Remplacer
push.archiver/push.publier_copie_locale(écriture NAS + manifeste + verrou immédiats) parpush.publier_localement(copie vérifiée vers l'espace de travail local uniquement, aucun manifeste/verrou/écriture NAS) —CollisionNomArchiveErrorrenomméeCollisionNomLocalError. Ancien fichier de testtest_push_archiver.pyremplacé partest_push_publier_localement.py(5/5 tests) - T042 Mettre à jour
test_pipeline_import_simple.py(intégration) : ajoute l'étape de classement, publie versdossier_localplutôt quedossier_archive, vérifie explicitement que l'archive n'est jamais créée - T043 Mettre à jour
packages/regine-cli/src/regine_cli/import_cmd.py:classer_par_formatavantattribuer_identifiants,publier_localementremplacearchiver+publier_copie_locale, message final indiquant la commanderegine reconcileà lancer plus tard pour archiver - T044 Mettre à jour
packages/regine-gui/src/regine_gui/import_view.py: import ne bloque plus si le partage SMB n'est pas monté (seul son chemin, viachemin_local_du_partage, sert au calcul — plusresoudre_archive_locale, qui exigeait une disponibilité réelle) ;_ArchivageWorkerrenommé_PublicationLocaleWorker, regroupant désormais classement + identifiants + publication locale sur le thread d'arrière-plan (déplacement supplémentaire par rapport à la première version de US3, qui ne déplaçait que l'ancienarchiver/publier_copie_locale) ; dialogues et journal reformulés (« Confirmer la copie de travail locale », jamais « archivage ») - T045 [P]
ruff check --fix/ruff formatsurpackages/regine-core,packages/regine-cli,packages/regine-gui; validation manuelle en mode headless (import complet sans archive configurée/montée, structureraw//jpeg/obtenue, aucune écriture sous/Volumes/...) — 176/176 testsregine-core/regine-cliau vert - T046 Mettre à jour
spec.md(Clarifications, User Story 1, FR-018/019, nouveau FR-020, Key Entities, Success Criteria SC-002/006/008 reformulés + nouveaux SC-009/SC-010, Assumptions),data-model.md,contracts/cli-import.md,quickstart.md,checklists/requirements.mdpour refléter cette révision
Checkpoint : l'import se termine par une copie de travail locale classée par format, jamais par une écriture sur l'archive ; l'archivage réutilise regine reconcile (specs/005-checkout-reconciliation) sans aucune modification de ce dernier.
Phase 8: Révision 2026-09-22 (bis) — détection de doublons entre imports (FR-004 révisé)
Contexte : test manuel de l'utilisateur — importer deux fois la même carte vers deux dossiers différents n'affichait aucun avertissement. Cause racine trouvée : le paramètre checksums_deja_importes de copie.copier_carte (déjà en place et testé isolément depuis T009) n'était jamais renseigné par aucun appelant réel — ni regine_cli.import_cmd, ni regine_gui.import_view. La déduplication ne fonctionnait donc dans aucun cas réel, y compris le cas initialement visé par FR-004 (carte non effacée entre deux imports).
- T047 [P] Créer
regine_core.import_carte.historique(enregistrer_import,rechercher_doublons) — nouvelle tablechecksums_importesdans la base de contexte centralisée (regine_core.config.db,specs/003), même schéma d'implantation quecamera_profile.dbpourboitiers. Testé danspackages/regine-core/tests/unit/test_historique.py(4/4 tests : recherche vide, retrouve un doublon, idempotence/mise à jour du dossier, ignore les checksums inconnus) - T048 [US1] Étendre
packages/regine-cli/src/regine_cli/import_cmd.py:connouvert avantcopier_carte(au lieu d'après) pour permettrerechercher_doublons(conn, ...)juste après la copie ;_confirmer_malgre_doublonsaffiche les fichiers/dossiers concernés et demande confirmation (continue sans bloquer en mode--yes, mais reste visible sur stdout) ;enregistrer_importappelé juste après chaquepublier_localementréussi - T049 [US3] Étendre
packages/regine-gui/src/regine_gui/import_view.py:_confirmer_malgre_doublons(QMessageBox) aprèscopier_cartedans_analyser;_checksums_en_coursmémorisé comme_titre_en_cours/_dossier_archive_en_coursavant de lancer le worker,enregistrer_importappelé dans_sur_publication_succes(thread principal, jamais dans le worker — même précaution que pour la connexion SQLite déjà rencontrée en Phase 7) - T050 [P] Validé manuellement en mode headless : script import et deux fois la même carte (fake RAW+JPEG) vers deux dossiers différents (« DossierA » puis « DossierB ») — le second import affiche bien l'avertissement de doublon avec le chemin du premier dossier, avant la confirmation de copie locale.
ruff check --fix/ruff formatsur les trois paquets — 180/180 testsregine-core/regine-cliau vert - T051 Mettre à jour
spec.md(Clarifications, FR-004 réécrite, Edge Case, nouvelle entité Historique des imports, SC-011, Assumptions),data-model.md,contracts/cli-import.md,contracts/regine-core-api.mdpour refléter cette révision
Checkpoint : un import détecte désormais tout fichier déjà importé précédemment (même carte non effacée, ou dossier de destination différent d'un import antérieur) et en informe l'utilisateur avant de poursuivre — plus jamais une exclusion ou une inclusion silencieuse.
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1) : dépend des tasks.md de
specs/004(packages, dossier.root, config.categories) déjà exécutées — sinon, à exécuter en premier. - Foundational (Phase 2) : dépend de Setup — BLOQUE toutes les user stories.
- User Stories (Phase 3–5) : dépendent toutes de Foundational.
- US1 (P1) : aucune dépendance sur une autre user story de ce fichier ; dépend de
specs/004(déjà disponible). - US2 (P2) : étend directement les fonctions de US1 (T014, T023/T024) — livrable séparément mais techniquement postérieure à US1.
- US3 (P3) : étend
destination.pyde US1 (T015 → T030) ; sa branche de fusion vers l'archive (T033,specs/005) et sa désambiguïsation de boîtiers (T034,specs/002) sont désormais toutes deux activables, ces deux specs étant implémentées (2026-09-19).
- US1 (P1) : aucune dépendance sur une autre user story de ce fichier ; dépend de
- Polish (Phase 6) : dépend des user stories livrées (au minimum US1).
Parallel Opportunities
- T002–T004 (Phase 1) : fichiers/dossiers distincts.
- T008 (Phase 2) : peut suivre T006/T007 en parallèle d'autres tâches de Phase 2 déjà terminées.
- Tests marqués [P] au sein de chaque story (T009–T011, T020/T021, T026/T027) : fichiers distincts.
Parallel Example: User Story 1
# Lancer les tests de la User Story 1 ensemble :
Task: "Test unitaire copie vérifiée dans packages/regine-core/tests/unit/test_copie_checksum.py"
Task: "Test unitaire découpage groupe unique dans packages/regine-core/tests/unit/test_groupage_dates.py"
Task: "Test unitaire construction nom dossier dans packages/regine-core/tests/unit/test_nommage.py"
Implementation Strategy
MVP First (User Story 1 uniquement)
- Vérifier/compléter les prérequis de
specs/004(Phase 1). - Compléter Phase 2 (Foundational).
- Compléter Phase 3 (US1) : import simple vers un nouveau dossier par défaut (année).
- Arrêt et validation : exécuter le Scénario 1 de
quickstart.md.
Livraison incrémentale
- Setup + Foundational → socle prêt.
- US1 → import simple (MVP) → valider Scénario 1.
- US2 → découpage multi-jours → valider Scénario 2.
- US3 → voyage multi-étapes → valider Scénario 3, y compris la fusion vers un dossier déjà archivé (T033) et la désambiguïsation de boîtiers (T034), toutes deux activables (
specs/005etspecs/002implémentées le 2026-09-19).
Notes
- [P] = fichiers différents, sans dépendance non résolue.
- Chaque user story est livrable et testable indépendamment ; T033/T034 (US3) dépendaient de
specs/005/specs/002, toutes deux implémentées (2026-09-19) — plus aucune dépendance externe non résolue dans ce fichier. - Committer après chaque tâche ou groupe logique de tâches.
- Ne pas recréer le squelette monorepo ni
regine_core.dossier.root/regine_core.config: ils viennent despecs/004-categorisation-dossiers/tasks.md, à étendre si besoin, jamais dupliquer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
|