Ajoute les thèmes communs sur les fiches de livres

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
LIMONIER Mathieu
2026-08-20 14:38:19 +02:00
co-authored by Copilot
parent e28d5bce32
commit c2d408afc6
17 changed files with 992 additions and 8 deletions
@@ -72,7 +72,9 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
// AsNoTracking : lecture pure. Et surtout : AUCUN filtre sur AjoutePar,
// la bibliothèque est commune à tout le foyer (CLAUDE.md).
var requete = FiltreLivres.Appliquer(
db.Livres.AsNoTracking().Include(l => l.Auteurs).ThenInclude(la => la.Auteur),
db.Livres.AsNoTracking()
.Include(l => l.Auteurs).ThenInclude(la => la.Auteur)
.Include(l => l.Themes).ThenInclude(lt => lt.Theme),
criteres,
utilisateur);
@@ -92,6 +94,7 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
var livre = await db.Livres
.AsNoTracking()
.Include(l => l.Auteurs).ThenInclude(la => la.Auteur)
.Include(l => l.Themes).ThenInclude(lt => lt.Theme)
.FirstOrDefaultAsync(l => l.Id == id, ct);
if (livre is null)
@@ -143,6 +146,7 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
db.Livres.Add(livre);
await RattacherAuteursAsync(livre, saisie.Auteurs, ct);
await RattacherThemesAsync(livre, saisie.Themes, ct);
await db.SaveChangesAsync(ct);
// Le statut n'est écrit que si l'utilisateur en a explicitement posé un : un livre sans
@@ -155,7 +159,10 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
public async Task<ResultatEcriture> ModifierAsync(
int id, EnregistrementLivre saisie, string? utilisateur, CancellationToken ct = default)
{
var livre = await db.Livres.Include(l => l.Auteurs).FirstOrDefaultAsync(l => l.Id == id, ct);
var livre = await db.Livres
.Include(l => l.Auteurs)
.Include(l => l.Themes).ThenInclude(lt => lt.Theme)
.FirstOrDefaultAsync(l => l.Id == id, ct);
if (livre is null)
{
return ResultatEcriture.Introuvable;
@@ -179,6 +186,7 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
// d'origine, pas des champs éditables.
await RattacherAuteursAsync(livre, saisie.Auteurs, ct);
await RattacherThemesAsync(livre, saisie.Themes, ct);
await db.SaveChangesAsync(ct);
// Le statut modifié est celui de la personne qui édite, pas de celle qui a saisi le livre.
@@ -287,7 +295,9 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
}
private IQueryable<Livre> AvecAuteurs() =>
db.Livres.AsNoTracking().Include(l => l.Auteurs).ThenInclude(la => la.Auteur);
db.Livres.AsNoTracking()
.Include(l => l.Auteurs).ThenInclude(la => la.Auteur)
.Include(l => l.Themes).ThenInclude(lt => lt.Theme);
/// <summary>Un des auteurs du livre catalogué est-il l'un de ceux qu'on est en train de saisir ?</summary>
/// <remarks>Le rôle n'entre pas dans la comparaison : c'est la personne qui compte ici.</remarks>
@@ -444,6 +454,37 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
}
}
private async Task RattacherThemesAsync(
Livre livre, IReadOnlyList<string> saisis, CancellationToken ct)
{
var themes = saisis
.Where(t => !string.IsNullOrWhiteSpace(t))
.Select(t => t.Trim())
.GroupBy(NormalisationTexte.Normaliser, StringComparer.Ordinal)
.Select(g => g.First())
.ToList();
if (livre.Themes.Count > 0)
{
db.LivreThemes.RemoveRange(livre.Themes);
livre.Themes.Clear();
}
foreach (var nom in themes)
{
var normalise = NormalisationTexte.Normaliser(nom);
var theme = await db.Themes.FirstOrDefaultAsync(t => t.NomNormalise == normalise, ct);
if (theme is null)
{
theme = new Theme { Nom = nom };
theme.RecalculerFormes();
db.Themes.Add(theme);
}
livre.Themes.Add(new LivreTheme { Livre = livre, Theme = theme });
}
}
/// <summary>
/// Contrôles minimaux : un titre, et un ISBN qui tienne debout s'il est fourni.
/// </summary>
@@ -491,6 +532,11 @@ public sealed class ServiceCatalogue(MaBibliDbContext db, IServiceAuteurs auteur
.Where(la => la.Auteur is not null)
.Select(la => new AuteurDto { Id = la.Auteur!.Id, Nom = la.Auteur.Nom, Role = la.Role })
.ToList(),
Themes = livre.Themes
.Where(lt => lt.Theme is not null)
.Select(lt => lt.Theme!.Nom)
.OrderBy(nom => NormalisationTexte.Normaliser(nom), StringComparer.Ordinal)
.ToList(),
Editeur = livre.Editeur,
Format = livre.Format,
TypeDocument = livre.TypeDocument,