Files
mabibli/MaBibli.Shared/Textes/CleOeuvre.cs
T
mathieuandClaude Opus 5 8b8fe3be6e Ajouter la liste d'envies personnelle, son export et la bibliographie par auteur
La liste d'envies vit dans une table séparée (LivreSouhaite) plutôt que dans un
statut de plus sur Livre : un livre souhaité n'est pas possédé, et le loger dans
Livres l'aurait fait entrer dans le catalogue, les compteurs et les prêts, au
prix d'un « et qui n'est pas souhaité » à répéter dans chaque lecture. La portée
est personnelle, comme le statut de lecture — mais ici Utilisateur est une vraie
frontière : toute lecture filtre dessus.

Bibliographie : SRU BnF interrogé par bib.author, vérifié le 2026-08-18. Deux
filtres mesurés sur des réponses réelles sont indispensables — le type de
document (l'index mêle livres audio, jeux et spectacles) et surtout l'auteur
réel de la notice, « all » rapprochant les mots sur l'ensemble des auteurs :
« Émile Zola » remonte sinon toute l'œuvre de sa fille Denise Le Blond-Zola.
Les rééditions sont regroupées par clé d'œuvre (183 notices Werber -> 51
œuvres). Le rapprochement avec l'étagère se fait par titre, pas par ISBN, qui
désigne une édition et non une œuvre ; ses limites sont dites à l'écran.

Export en deux formats, tous deux du texte sans dépendance : .txt groupé par
auteur pour la librairie, .csv à séparateur point-virgule et BOM pour le
tableur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:59:06 +02:00

75 lines
3.6 KiB
C#

namespace MaBibli.Shared.Textes;
/// <summary>
/// Clé d'identification d'une <b>œuvre</b> à partir d'un titre, indépendamment de l'édition.
/// </summary>
/// <remarks>
/// C'est le rapprochement le moins mauvais dont on dispose, et il faut être clair sur ses limites.
/// <para>
/// <b>Pourquoi pas l'ISBN ?</b> Parce qu'il désigne une <i>édition</i>, pas une œuvre : le poche
/// de 1998, le grand format de 1991 et la réédition de 2015 portent trois ISBN différents pour le
/// même roman. Mesuré sur la bibliographie BnF de Bernard Werber : 183 notices de livres pour
/// 51 œuvres, chacune revenant en moyenne trois fois sous trois ISBN. Comparer les ISBN
/// répondrait donc « vous ne l'avez pas » à propos d'un livre posé sur l'étagère, ce qui est le
/// pire résultat possible pour une liste d'envies.
/// </para>
/// <para>
/// <b>Ce que la clé fait.</b> Elle coupe la mention de responsabilité ISBD (« <c> / </c> ») puis
/// le sous-titre (« <c> : </c> »), et met le reste à plat. C'est ce qui réunit
/// « Les thanatonautes », « Les thanatonautes : roman » et
/// « Les thanatonautes / Bernard Werber ».
/// </para>
/// <para>
/// <b>Ce qu'elle rate</b>, et qu'il ne faut pas prétendre autrement :
/// <list type="bullet">
/// <item>Deux œuvres distinctes partageant un titre principal sont confondues (« Œuvres »,
/// « Nouvelles »). Le risque est borné : la comparaison n'a lieu qu'<b>à auteur donné</b>.</item>
/// <item>Un tome n'est pas rapproché de son recueil : « Troisième humanité » et
/// « Troisième humanité. Tome 1 » restent deux lignes. Le point n'est pas coupé, faute de savoir
/// distinguer « Tome 1 » d'un titre qui contient légitimement un point.</item>
/// <item>Un titre retraduit ou changé au fil des rééditions n'est pas rapproché du tout.</item>
/// <item>Une œuvre possédée à l'intérieur d'une intégrale n'est pas repérée : l'intégrale a son
/// propre titre.</item>
/// </list>
/// Conséquence assumée : l'écran de bibliographie peut afficher comme « à découvrir » un livre
/// déjà présent sous un autre titre. Il ne fait donc que <b>griser</b> ce qu'il reconnaît, sans
/// jamais masquer une ligne — un faux négatif se voit et se corrige d'un coup d'œil, alors qu'une
/// ligne masquée à tort serait invisible.
/// </para>
/// </remarks>
public static class CleOeuvre
{
/// <summary>Sépare le titre de la mention de responsabilité : « Germinal / Émile Zola ».</summary>
private const string SeparateurResponsabilite = " / ";
/// <summary>Sépare le titre de son sous-titre : « Les thanatonautes : roman ».</summary>
private const string SeparateurSousTitre = " : ";
/// <summary>
/// Clé comparable d'un titre : « Les thanatonautes : roman » → <c>les thanatonautes</c>.
/// </summary>
public static string Cle(string? titre)
{
var s = titre?.Trim();
if (string.IsNullOrEmpty(s))
{
return string.Empty;
}
s = Tronquer(s, SeparateurResponsabilite);
var sansSousTitre = Tronquer(s, SeparateurSousTitre);
// Un titre qui n'est QUE son sous-titre (« : roman ») ne doit pas se réduire à rien :
// mieux vaut alors garder la chaîne entière que produire une clé vide, qui se
// confondrait avec toutes les autres clés vides.
var retenu = NormalisationTexte.Normaliser(sansSousTitre);
return retenu.Length > 0 ? retenu : NormalisationTexte.Normaliser(s);
}
private static string Tronquer(string s, string separateur)
{
var i = s.IndexOf(separateur, StringComparison.Ordinal);
return i >= 0 ? s[..i].Trim() : s;
}
}