Distinguer scénario, dessin et couleurs sur chaque livre
Le rôle vit sur le LIEN livre-auteur, jamais sur l'auteur : le même auteur scénarise un album et dessine le suivant, et il ne doit pas exister en deux fiches pour autant — ce que l'index unique sur la clé de regroupement interdit de toute façon. Les rôles se retrouvent par clé d'auteur résolue et non par position : ResoudreAsync écarte les noms vides et fusionne les variantes, donc sa liste peut être plus courte que la saisie. Piège traité : le champ des noms reconstruit la liste à chaque frappe. Decouper reprend les rôles connus en rapprochant sur la forme normalisée, sans quoi corriger une faute de frappe les effacerait tous. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using MaBibli.Shared.Entites;
|
||||
using MaBibli.Shared.Textes;
|
||||
|
||||
namespace MaBibli.Shared.Dtos;
|
||||
|
||||
@@ -25,10 +26,10 @@ public record EnregistrementLivre
|
||||
public string Titre { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Noms des auteurs, dans l'ordre de la couverture. Le serveur les rattache aux fiches
|
||||
/// existantes quand c'est sûr, et en crée sinon.
|
||||
/// Auteurs, dans l'ordre de la couverture, chacun avec son rôle sur <b>ce</b> livre. Le
|
||||
/// serveur les rattache aux fiches existantes quand c'est sûr, et en crée sinon.
|
||||
/// </summary>
|
||||
public List<string> Auteurs { get; set; } = [];
|
||||
public List<AuteurSaisi> Auteurs { get; set; } = [];
|
||||
|
||||
public string? Editeur { get; set; }
|
||||
|
||||
@@ -57,18 +58,51 @@ public record EnregistrementLivre
|
||||
[JsonIgnore]
|
||||
public string? Auteur
|
||||
{
|
||||
get => Auteurs.Count == 0 ? null : string.Join($"{SeparateurAuteurs} ", Auteurs);
|
||||
set => Auteurs = Decouper(value);
|
||||
get => Auteurs.Count == 0 ? null : string.Join($"{SeparateurAuteurs} ", Auteurs.Select(a => a.Nom));
|
||||
set => Auteurs = Decouper(value, Auteurs);
|
||||
}
|
||||
|
||||
/// <summary>Découpe une saisie texte en noms d'auteurs, en écartant les entrées vides.</summary>
|
||||
public static List<string> Decouper(string? saisie) =>
|
||||
string.IsNullOrWhiteSpace(saisie)
|
||||
? []
|
||||
: saisie.Split(SeparateurAuteurs, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.ToList();
|
||||
/// <summary>
|
||||
/// Découpe une saisie texte en auteurs, en écartant les entrées vides.
|
||||
/// </summary>
|
||||
/// <param name="connus">
|
||||
/// Auteurs déjà saisis, dont les <b>rôles</b> sont repris quand le nom réapparaît.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// ⚠️ Sans cette reprise, corriger une faute de frappe dans le champ des noms effacerait le
|
||||
/// rôle des auteurs voisins : le champ se relie à chaque frappe, et il reconstruirait la
|
||||
/// liste entière à « non précisé ». Le rapprochement se fait sur la forme normalisée du nom,
|
||||
/// pour survivre à un accent ajouté après coup.
|
||||
/// </remarks>
|
||||
public static List<AuteurSaisi> Decouper(string? saisie, IEnumerable<AuteurSaisi>? connus = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(saisie))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var roles = new Dictionary<string, RoleAuteur>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var connu in connus ?? [])
|
||||
{
|
||||
roles.TryAdd(NormalisationTexte.Normaliser(connu.Nom), connu.Role);
|
||||
}
|
||||
|
||||
return saisie
|
||||
.Split(SeparateurAuteurs, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Select(nom => new AuteurSaisi(
|
||||
nom, roles.GetValueOrDefault(NormalisationTexte.Normaliser(nom), RoleAuteur.NonPrecise)))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Un auteur tel qu'il est saisi : son nom, et son rôle sur ce livre.</summary>
|
||||
/// <remarks>
|
||||
/// Le rôle n'appartient pas à la personne mais au <b>lien</b> : le même auteur scénarise un
|
||||
/// album et dessine le suivant. Voir <see cref="RoleAuteur"/>.
|
||||
/// </remarks>
|
||||
public record AuteurSaisi(string Nom, RoleAuteur Role = RoleAuteur.NonPrecise);
|
||||
|
||||
/// <summary>Charge utile du changement de statut de lecture — l'action la plus fréquente.</summary>
|
||||
/// <remarks>
|
||||
/// Le statut posé est toujours celui de l'appelant : il n'y a pas de champ « utilisateur »,
|
||||
|
||||
@@ -12,6 +12,13 @@ public record AuteurDto
|
||||
|
||||
/// <summary>Nombre de livres du catalogue rattachés à cet auteur. Renseigné par la liste des auteurs.</summary>
|
||||
public int NombreDeLivres { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rôle sur le livre où cet auteur figure. <b>N'a de sens qu'à l'intérieur d'un
|
||||
/// <see cref="LivreDto"/></b> : la liste des auteurs, elle, ne parle d'aucun livre en
|
||||
/// particulier et le laisse à <c>NonPrecise</c>.
|
||||
/// </summary>
|
||||
public RoleAuteur Role { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user