using System.Text.Json.Serialization; namespace MaBibli.Shared.Dtos; /// /// Une envie telle qu'exposée par l'API. /// /// /// Il n'y a aucun champ « utilisateur », et il ne doit jamais y en avoir : l'API ne rend /// que les envies de l'appelant, qu'elle identifie par les en-têtes SSOwat. Exposer le /// propriétaire laisserait croire qu'on peut demander celles d'un autre. /// public record SouhaitDto { public required int Id { get; init; } public required string Titre { get; init; } public string? Auteur { get; init; } public string? Editeur { get; init; } public string? Annee { get; init; } public string? Isbn { get; init; } public string? CoverUrl { get; init; } public string? Note { get; init; } public required DateTime DateAjout { get; init; } /// /// Vrai si un livre du catalogue correspond à cette envie. /// /// /// C'est un signalement, jamais une suppression. Le catalogue est commun, la liste /// d'envies personnelle : retirer l'envie de quelqu'un d'autre modifierait sa liste en /// silence et lui ferait perdre sa note (« demandé à Noël »). Le rapprochement est en outre /// faillible (voir CleOeuvre) — un signalement erroné s'ignore d'un coup d'œil, une /// suppression erronée ne se rattrape pas. /// public bool Possede { get; init; } /// Le livre du catalogue en cause, s'il y en a un : de quoi ouvrir sa fiche. public int? LivreId { get; init; } } /// Charge utile d'ajout d'une envie. /// /// Comme , elle ne porte pas d'identité : le propriétaire est /// déterminé par le serveur. /// public record EnregistrementSouhait { public string Titre { get; set; } = string.Empty; public string? Auteur { get; set; } public string? Editeur { get; set; } public string? Annee { get; set; } public string? Isbn { get; set; } public string? CoverUrl { get; set; } public string? Note { get; set; } } /// /// Une envie de revue — ou de numéro précis — telle qu'exposée par l'API. /// /// /// Comme , aucun champ « utilisateur » : l'API ne rend que les /// envies de l'appelant. /// /// Il n'y a volontairement ni « Possede » ni « RevueId », contrairement aux envies de /// livres : le rapprochement avec les revues possédées supposerait de décider ce que vaut /// « j'ai la revue mais pas ce numéro », et un signalement faux sur une liste d'envies est /// exactement ce qu'on cherche à éviter. Rien n'empêchera de l'ajouter le jour où le besoin /// sera constaté. /// /// public record RevueSouhaiteeDto { public required int Id { get; init; } public required string Titre { get; init; } /// null = la revue entière, pas un numéro particulier. public string? Numero { get; init; } /// ISSN sous sa forme à tiret, la seule rangée en base. public string? Issn { get; init; } public string? Note { get; init; } public required DateTime DateAjout { get; init; } /// Titre suivi de son numéro, tel qu'on l'épelle à un kiosquier. [JsonIgnore] public string TitreComplet => string.IsNullOrWhiteSpace(Numero) ? Titre : $"{Titre} n° {Numero}"; } /// Charge utile d'ajout ou de modification d'une envie de revue. /// Sans identité : le propriétaire est déterminé par le serveur. public record EnregistrementRevueSouhaitee { public string Titre { get; set; } = string.Empty; public string? Numero { get; set; } public string? Issn { get; set; } public string? Note { get; set; } } /// Charge utile du masquage personnel d'une œuvre bibliographique. public record MasquageBibliographie { public string Titre { get; set; } = string.Empty; } /// Formats d'export de la liste d'envies. /// /// Les deux usages décrits dans CLAUDE.md ne demandent pas la même chose : « l'emporter en /// librairie » veut un texte qui se lit tel quel sur un téléphone, « la partager avant un /// anniversaire » finit souvent dans un tableur pour se répartir les achats. Ni l'un ni l'autre /// n'exige de dépendance : ce sont deux fichiers texte produits par du string. /// public enum FormatExportSouhaits { /// Liste lisible telle quelle, groupée par auteur. Texte = 0, /// Tableau ouvrable dans un tableur. Csv = 1, } /// Une œuvre de la bibliographie d'un auteur, confrontée à ce que l'on possède déjà. public record OeuvreBibliographie { public required string Titre { get; init; } public string? Annee { get; init; } /// Année la plus récente des éditions BnF réunies sous cette œuvre. /// /// L'année affichée reste celle de l'édition la plus ancienne, qui représente l'œuvre. /// Cette seconde valeur sert uniquement à rechercher les nouveautés. /// public int? AnneeDerniereEdition { get; init; } public string? Editeur { get; init; } /// ISBN d'une des éditions relevées, quand la notice en portait un. public string? Isbn { get; init; } /// /// Nombre de notices BnF réunies sous cette œuvre — c'est-à-dire le nombre de rééditions /// relevées. Sert à repérer les œuvres majeures d'un auteur très réédité. /// public int NombreEditions { get; init; } /// /// Vrai si un livre du catalogue porte le même titre d'œuvre. /// Rapprochement par titre, donc faillible — voir CleOeuvre. /// public bool Possede { get; init; } /// Le livre possédé, s'il y en a un : de quoi ouvrir sa fiche. public int? LivreId { get; init; } /// Vrai si l'appelant a déjà cette œuvre dans sa liste d'envies. public bool Souhaite { get; init; } public int? SouhaitId { get; init; } /// Vrai si l'appelant a explicitement masqué cette œuvre. public bool Masquee { get; init; } /// Ni possédé, ni déjà souhaité : ce qui reste à découvrir. [JsonIgnore] public bool ADecouvrir => !Possede && !Souhaite; } /// /// Ce qu'il est advenu de l'interrogation de la BnF. /// /// /// ⚠️ Une source muette n'est pas une bibliographie vide, et la différence n'est pas /// cosmétique : sans cet état, un délai dépassé produisait une liste vide, que l'écran /// commentait d'un rassurant « la BnF ne connaît aucun livre de cet auteur ». On affirmait donc /// une chose qu'on n'avait pas pu vérifier. C'est ce que cet énuméré rend impossible. /// /// Le motif est distingué parce qu'il ne demande pas la même chose à l'utilisateur : un délai /// dépassé se retente, une réponse illisible non. /// /// public enum EtatSourceBibliographie { /// La BnF a répondu. La liste rendue est ce qu'elle sait, y compris si elle est vide. Ok = 0, /// La BnF n'a pas répondu à temps. Cas le plus fréquent, et le plus souvent passager. DelaiDepasse = 1, /// La BnF n'a pas pu être jointe, ou a répondu par une erreur. Injoignable = 2, /// La BnF a répondu, mais sa réponse n'était pas exploitable. ReponseIllisible = 3, } /// /// Bibliographie d'un auteur du catalogue, telle que la BnF la connaît. /// /// /// Les compteurs ne sont pas décoratifs : ils disent à l'utilisateur ce qu'il regarde. Sur un /// auteur très réédité (Émile Zola : 2692 notices), on ne montre qu'un extrait classé par /// pertinence, et le taire ferait passer une liste partielle pour une bibliographie complète. /// public record BibliographieDto { public required AuteurDto Auteur { get; init; } public IReadOnlyList Oeuvres { get; init; } = []; /// Nombre total de notices que la BnF déclare pour cet auteur. public int NoticesAnnoncees { get; init; } /// Nombre de notices réellement lues (plafonné). public int NoticesLues { get; init; } /// Notices écartées parce qu'aucun de leurs auteurs n'est celui demandé. public int NoticesEcartees { get; init; } /// Vrai si la BnF en annonce plus qu'on n'en a lu : la liste est un extrait. public bool Tronquee => NoticesAnnoncees > NoticesLues; /// Message à afficher quand la source n'a pas pu être interrogée normalement. public string? Avertissement { get; init; } /// /// Ce qu'il est advenu de l'interrogation. ne veut /// pas dire « des résultats », mais « une réponse ». /// public EtatSourceBibliographie Etat { get; init; } /// Vrai quand la liste est limitée aux œuvres postérieures au seuil possédé. public bool EstNouveautes { get; init; } /// Année de publication la plus récente retrouvée pour un livre possédé. public int? AnneeSeuilNouveautes { get; init; } /// Vrai quand aucun ISBN ou titre possédé n'a permis de dater le seuil. public bool SeuilNouveautesInconnu { get; init; } /// /// Vrai quand la BnF n'a pas répondu : la liste vide ne prouve alors rien. /// /// /// C'est ce que l'interface doit consulter avant d'expliquer une absence de résultats. /// public bool SourceMuette => Etat != EtatSourceBibliographie.Ok; }