MaBibli 1.0.0
Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts, scan de code-barres, consultation hors-ligne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
using MaBibli.Shared.Dtos;
|
||||
|
||||
namespace MaBibli.Api.Services.Isbn;
|
||||
|
||||
/// <summary>
|
||||
/// Transformation d'une édition OpenLibrary en candidat. Fonctions pures, testables sans réseau.
|
||||
/// </summary>
|
||||
public static class OpenLibraryMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Clés d'auteurs portées par l'édition elle-même (<c>/authors/OL…A</c>), éventuellement vide.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ces clés ne sont <b>pas</b> des noms : chacune impose un appel à
|
||||
/// <c>/authors/{id}.json</c>. Oublier ce second appel donne un titre rempli et un auteur vide
|
||||
/// — symptôme exact observé chez BookLogr.
|
||||
/// </remarks>
|
||||
public static IReadOnlyList<string> ClesAuteurs(OpenLibraryEdition edition) =>
|
||||
edition.Authors?
|
||||
.Select(a => a.Key)
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.Select(k => k!)
|
||||
.ToList() ?? [];
|
||||
|
||||
/// <summary>Clés d'auteurs portées par l'œuvre, utilisées en repli.</summary>
|
||||
public static IReadOnlyList<string> ClesAuteurs(OpenLibraryWork work) =>
|
||||
work.Authors?
|
||||
.Select(a => a.Author?.Key)
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.Select(k => k!)
|
||||
.ToList() ?? [];
|
||||
|
||||
/// <summary>Clé de l'œuvre rattachée (<c>/works/OL…W</c>), ou <c>null</c>.</summary>
|
||||
public static string? CleOeuvre(OpenLibraryEdition edition) =>
|
||||
edition.Works?.Select(w => w.Key).FirstOrDefault(k => !string.IsNullOrWhiteSpace(k));
|
||||
|
||||
/// <summary>Nom affichable d'un auteur OpenLibrary.</summary>
|
||||
public static string? Nom(OpenLibraryAuthor auteur)
|
||||
{
|
||||
var nom = auteur.Name ?? auteur.PersonalName;
|
||||
return string.IsNullOrWhiteSpace(nom) ? null : nom.Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assemble le candidat final. <paramref name="nomsAuteurs"/> vient des appels
|
||||
/// <c>/authors/{id}.json</c> déjà effectués par le client.
|
||||
/// </summary>
|
||||
public static CandidatLivre? VersCandidat(
|
||||
OpenLibraryEdition edition,
|
||||
IReadOnlyList<string> nomsAuteurs,
|
||||
string isbnInterroge,
|
||||
string? urlCouverture)
|
||||
{
|
||||
var titre = edition.Title?.Trim();
|
||||
if (string.IsNullOrEmpty(titre))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(edition.Subtitle))
|
||||
{
|
||||
titre = $"{titre} : {edition.Subtitle.Trim()}";
|
||||
}
|
||||
|
||||
// Les noms résolus par /authors/{id}.json arrivent déjà séparés. En dernier repli la
|
||||
// mention de responsabilité n'est qu'une phrase : on la garde entière plutôt que de
|
||||
// parier sur ses virgules, qui séparent aussi bien deux auteurs qu'un « Zola, Émile ».
|
||||
var auteurs = nomsAuteurs.Count > 0
|
||||
? nomsAuteurs
|
||||
: Liste(NettoyerByStatement(edition.ByStatement));
|
||||
|
||||
return new CandidatLivre
|
||||
{
|
||||
Titre = titre,
|
||||
Auteurs = auteurs,
|
||||
Editeur = edition.Publishers?.FirstOrDefault()?.Trim().Trim('[', ']'),
|
||||
Annee = string.IsNullOrWhiteSpace(edition.PublishDate) ? null : edition.PublishDate.Trim(),
|
||||
Langue = edition.Languages?
|
||||
.Select(l => l.Key)
|
||||
.FirstOrDefault(k => !string.IsNullOrWhiteSpace(k))?
|
||||
.Replace("/languages/", string.Empty),
|
||||
CoverUrl = urlCouverture,
|
||||
Source = SourceMetadonnees.OpenLibrary,
|
||||
IsbnInterroge = isbnInterroge,
|
||||
IdentifiantSource = edition.Key,
|
||||
UrlNotice = UrlNotice(edition.Key),
|
||||
};
|
||||
}
|
||||
|
||||
private static string? UrlNotice(string? cle) =>
|
||||
string.IsNullOrWhiteSpace(cle)
|
||||
? null
|
||||
: $"https://openlibrary.org/{cle.TrimStart('/')}";
|
||||
|
||||
private static IReadOnlyList<string> Liste(string? valeur) =>
|
||||
string.IsNullOrWhiteSpace(valeur) ? [] : [valeur];
|
||||
|
||||
/// <summary>
|
||||
/// Dernier repli quand aucun auteur n'est résolvable : la mention de responsabilité brute,
|
||||
/// débarrassée de sa ponctuation finale (« Thomas H. Cormen ... [et al.]. »).
|
||||
/// </summary>
|
||||
private static string? NettoyerByStatement(string? brut)
|
||||
{
|
||||
var s = brut?.Trim();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var i = s.IndexOf(" ... ", StringComparison.Ordinal);
|
||||
if (i >= 0)
|
||||
{
|
||||
s = s[..i];
|
||||
}
|
||||
|
||||
s = s.TrimEnd('.', ' ', ',');
|
||||
return s.Length == 0 ? null : s;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user