Fusionner la phase base de donnees (statut par utilisateur, table Auteur)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mathieu
2026-08-18 02:24:09 +02:00
co-authored by Claude Opus 5
38 changed files with 3377 additions and 168 deletions
+51 -4
View File
@@ -310,20 +310,47 @@ Compilation **locale**, puis dépôt manuel de l'archive en release sur le Gitea
**Ne jamais compiler sur le serveur** à l'installation : cela imposerait le SDK .NET complet sur la machine YunoHost, pour une compilation lente — l'inverse exact de ce que permet le self-contained.
## Modèle de données (base de départ, à affiner)
## Modèle de données
Schéma en vigueur depuis la migration `StatutPersonnelEtTableAuteurs` (2026-08-18).
```
Livre
├── Id
├── Isbn
├── Isbn (index)
├── Titre
├── Auteur
├── TitreNormalise (index — minuscules sans accents, recalculé à chaque écriture)
├── Editeur
├── Format : Physique | Numerique
├── Statut : ALire | EnCours | Lu
├── CoverUrl
├── DateAjout
└── AjoutePar (YNH_USER — traçabilité, PAS un cloisonnement)
⚠ pas de colonne Statut, pas de colonne Auteur
Auteur
├── Id
├── Nom (affichage : « Émile Zola »)
├── NomNormalise (index — recherche : « emile zola »)
└── CleRegroupement (index UNIQUE — mots normalisés triés : « emile zola »)
LivreAuteur (n-n : un livre peut avoir plusieurs auteurs)
├── LivreId ─┐ clé primaire composite
├── AuteurId ─┘ FK Livre = cascade, FK Auteur = restrict
└── Position (rang sur la couverture, à partir de 0)
StatutLecture (le statut est PERSONNEL)
├── Id
├── LivreId (FK vers Livre, cascade)
├── Utilisateur (YNH_USER)
├── Statut : ALire | EnCours | Lu
├── DateMaj
└── UNIQUE (LivreId, Utilisateur)
absence de ligne = « non commencé »
RapprochementRefuse (mémoire des « non » de l'utilisateur)
├── Id
├── AuteurAId ─┐ UNIQUE, couple toujours rangé (petit Id d'abord)
└── AuteurBId ─┘ pour qu'un refus vaille dans les deux sens
Pret
├── Id
@@ -341,6 +368,26 @@ Garder `Pret` comme table séparée (pas un champ sur `Livre`) pour conserver l'
**L'auteur n'est plus un champ texte sur `Livre`.** Une table dédiée porte le nom d'affichage et une forme normalisée servant au regroupement et à la recherche. Le regroupement automatique ne s'applique qu'aux variantes **sûres** (casse, accents, initiales, ordre nom/prénom) ; les rapprochements ambigus — « Hamilton » seul vers « Peter F. Hamilton » — doivent être **proposés**, jamais appliqués silencieusement : une fusion erronée est difficile à défaire.
### Recherche sans accents : pourquoi des colonnes normalisées
**SQLite ne sait pas ignorer les accents.** Sa collation `NOCASE` ne couvre que l'ASCII et il n'existe pas de `unaccent()` intégré. La seule façon d'obtenir « Emile » → « Émile » sans charger tout le catalogue en mémoire est de stocker la forme mise à plat dans une colonne dédiée, et d'y chercher un terme passé par **exactement la même** fonction (`NormalisationTexte`).
Conséquence à ne pas oublier : **toute écriture doit recalculer ces colonnes** (`Livre.RecalculerFormes()`, `Auteur.RecalculerFormes()`).
Deux formes cohabitent sur `Auteur`, et ce n'est pas une redondance :
- `NomNormalise` garde l'ordre de saisie — c'est ce que la recherche parcourt en sous-chaîne ;
- `CleRegroupement` trie les mots, ce qui rend `Zola, Émile` et `Émile Zola` identiques. C'est elle qui porte l'index **unique**, donc l'invariant « un auteur, une fiche ».
La clé ne sait pas rapprocher les **initiales** (`P.F. Hamilton``Peter F. Hamilton`) : cela demande d'aligner les mots un à un, ce qui n'a pas d'équivalent SQL. Ce cas est traité en mémoire par `RapprochementAuteurs.Comparer`, sur une table qui compte au plus quelques centaines de lignes.
### Reprise des données existantes
La migration recopie l'ancien contenu avec les moyens du bord, puis **`ServiceRenormalisation` finit le travail en C# au démarrage** — idempotent, il sert aussi de filet si les règles de normalisation changent un jour.
- L'ancien `Livres.Statut`, commun à tout le foyer, est **rattaché à `AjoutePar`**, seule personne que la base associe au livre. Les statuts des livres sans `AjoutePar` sont **perdus** : les attribuer serait une invention.
- L'ancien `Livres.Auteur` devient une fiche par valeur distincte. SQLite ne regroupant qu'à la casse près, les variantes accentuées ressortent en double — c'est le passage C# qui les réunit, en gardant le nom d'affichage le plus présentable (accents conservés, ni `NOM, Prénom` ni capitales intégrales).
**Les prêts ne concernent en pratique que les livres physiques** — les ebooks étant de simples fiches, il n'y a pas d'objet à prêter. `Emprunteur` reste un **texte libre**, sans lien avec les comptes YunoHost : on suit les prêts à des personnes extérieures au foyer, pas les échanges entre utilisateurs de l'app.
## Historique du projet (pourquoi ces choix)