more stuff for TP2, including database

master
Marc CHEVALDONNE 2 years ago
parent 3319b48d91
commit 7d576121c8

@ -29,14 +29,41 @@ namespace JsonReader
Id = (string)o["key"], Id = (string)o["key"],
Name = (string)o["name"], Name = (string)o["name"],
Bio = bioTokenAsString, Bio = bioTokenAsString,
BirthDate = o.TryGetValue("birth_date", out JToken? bd) ? DateTime.ParseExact((string)bd, "d MMMM yyyy", CultureInfo.InvariantCulture) : null, BirthDate = o.TryGetValue("birth_date", out JToken? bd) ? ReadDate((string)o["birth_date"]) : null,
DeathDate = o.TryGetValue("death_date", out JToken? dd) ? DateTime.ParseExact((string)dd, "d MMMM yyyy", CultureInfo.InvariantCulture) : null, DeathDate = o.TryGetValue("death_date", out JToken? dd) ? ReadDate((string)o["death_date"]) : null,
Links = o.TryGetValue("links", out JToken? links) ? links.Select(l => new LinkDTO { Title = (string)l["title"], Url = (string)l["url"] }).ToList() : new List<LinkDTO>(), Links = o.TryGetValue("links", out JToken? links) ? links.Select(l => new LinkDTO { Title = (string)l["title"], Url = (string)l["url"] }).ToList() : new List<LinkDTO>(),
AlternateNames = o.TryGetValue("alternate_names", out JToken? altNames) ? altNames.Select(alt => (string)alt).ToList() : new List<string?>() AlternateNames = o.TryGetValue("alternate_names", out JToken? altNames) ? altNames.Select(alt => (string)alt).ToList() : new List<string?>()
}; };
return author; return author;
} }
public static DateTime? ReadDate(string dateInJson)
{
if(dateInJson == null) return null;
List<Tuple<string, CultureInfo>> pubDateFormat =new List<Tuple<string, CultureInfo>>()
{
Tuple.Create("d MMMM yyyy", CultureInfo.GetCultureInfo("fr-FR")),
Tuple.Create("d MMMM yyyy", CultureInfo.InvariantCulture),
Tuple.Create("MMM dd, yyyy", CultureInfo.InvariantCulture)
};
DateTime? publishDate = null;
foreach(var format in pubDateFormat)
{
if(DateTime.TryParseExact(dateInJson, format.Item1, format.Item2, DateTimeStyles.None, out DateTime readDate))
{
publishDate = readDate;
break;
}
}
if(!publishDate.HasValue && int.TryParse(dateInJson, out int year))
{
publishDate = new DateTime(year, 12, 31);
}
return publishDate;
}
public static Tuple<long, IEnumerable<AuthorDTO>> GetAuthorsByName(string json) public static Tuple<long, IEnumerable<AuthorDTO>> GetAuthorsByName(string json)
{ {
JObject o = JObject.Parse(json); JObject o = JObject.Parse(json);

@ -20,20 +20,35 @@ public static class BookJsonReader
JObject o = JObject.Parse(json); JObject o = JObject.Parse(json);
var l = o["languages"]?.FirstOrDefault(""); var l = o["languages"]?.FirstOrDefault("");
Languages lang = l != null ? languages[(string)l["key"]] : Languages.Unknown; Languages lang = l != null ? languages[(string)l["key"]] : Languages.Unknown;
Tuple<string, CultureInfo> pubDateFormat = lang switch //List<Tuple<string, CultureInfo>> pubDateFormat =new List<Tuple<string, CultureInfo>>()
{ //{
Languages.French => Tuple.Create("d MMMM yyyy", CultureInfo.GetCultureInfo("fr-FR")), // Tuple.Create("d MMMM yyyy", CultureInfo.GetCultureInfo("fr-FR")),
Languages.Unknown => Tuple.Create("MMM dd, yyyy", CultureInfo.InvariantCulture) // Tuple.Create("MMM dd, yyyy", CultureInfo.InvariantCulture)
}; //};
//DateTime? publishDate = null;
//foreach(var format in pubDateFormat)
//{
// if(DateTime.TryParseExact((string)o["publish_date"], format.Item1, format.Item2, DateTimeStyles.None, out DateTime readDate))
// {
// publishDate = readDate;
// break;
// }
//}
//if(!publishDate.HasValue)
//{
// publishDate = new DateTime((int)o["publish_date"], 12, 31);
//}
DateTime? publishDate = AuthorJsonReader.ReadDate((string)o["publish_date"]);
BookDTO book = new BookDTO BookDTO book = new BookDTO
{ {
Id = (string)o["key"], Id = (string)o["key"],
Title = (string)o["title"], Title = (string)o["title"],
Publishers = o["publishers"].Select(p => (string)p).ToList(), Publishers = o["publishers"].Select(p => (string)p).ToList(),
PublishDate = DateTime.TryParseExact((string)o["publish_date"], pubDateFormat.Item1, pubDateFormat.Item2, DateTimeStyles.None, out DateTime date) ? date : new DateTime((int)o["publish_date"], 12, 31), PublishDate = publishDate.GetValueOrDefault(DateTime.Now),
ISBN13 = (string)o["isbn_13"][0], ISBN13 = (string)o["isbn_13"][0],
NbPages = (int)o["number_of_pages"], NbPages = o["number_of_pages"] != null ? (int)o["number_of_pages"] : -1,
Language = lang, Language = lang,
Format = o.TryGetValue("physical_format", out JToken? f) ? (string)f : null, Format = o.TryGetValue("physical_format", out JToken? f) ? (string)f : null,
Works = o["works"].Select(w => new WorkDTO { Id = (string)w["key"] }).ToList(), Works = o["works"].Select(w => new WorkDTO { Id = (string)w["key"] }).ToList(),

@ -23,12 +23,28 @@ namespace JsonReader
ratingsDto.Count = (int)r["summary"]["count"]; ratingsDto.Count = (int)r["summary"]["count"];
} }
string description = null;
if(o.TryGetValue("description", out JToken? descr))
{
if(descr.Type == JTokenType.String)
{
description = (string)descr;
}
else
{
if (descr["value"].Type == JTokenType.String)
{
description = (string)descr["value"];
}
}
}
WorkDTO work = new WorkDTO WorkDTO work = new WorkDTO
{ {
Id = (string)o["key"], Id = (string)o["key"],
Title = (string)o["title"], Title = (string)o["title"],
Authors = o["authors"].Select(a => new AuthorDTO { Id = (string)a["author"]["key"] }).ToList(), Authors = o.TryGetValue("authors", out JToken? authors) ? authors.Select(a => new AuthorDTO { Id = (string)a["author"]["key"] }).ToList() : new List<AuthorDTO>(),
Description = o.TryGetValue("description", out JToken? descr) ? (string)descr : null, Description = description,
Subjects = o.TryGetValue("subjects", out JToken? subjects) ? subjects.Select(s => (string)s).ToList() : new List<string>(), Subjects = o.TryGetValue("subjects", out JToken? subjects) ? subjects.Select(s => (string)s).ToList() : new List<string>(),
Ratings = ratingsDto Ratings = ratingsDto
}; };

@ -0,0 +1,89 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Extensions.Options;
using MyLibraryEntities;
namespace MyLibraryDB;
public class MyLibraryContext : DbContext
{
public DbSet<AuthorEntity> Authors { get; set; }
public DbSet<BookEntity> Books { get; set; }
public DbSet<WorkEntity> Works { get; set; }
public MyLibraryContext()
{ }
public MyLibraryContext(DbContextOptions<MyLibraryContext> options)
: base(options)
{ }
private static DbContextOptions<MyLibraryContext> InitPlaftormDB(string dbPlatformPath)
{
var options = new DbContextOptionsBuilder<MyLibraryContext>()
.UseMySql($"{dbPlatformPath}", new MySqlServerVersion(new Version(10, 11, 1)))
.Options;
return options;
}
public MyLibraryContext(string dbPlatformPath)
: this(InitPlaftormDB(dbPlatformPath))
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlite($"Data Source=MylibraryDB.db");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AuthorEntity>()
.Property(e => e.AlternateNames)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null),
new ValueComparer<ICollection<string>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (ICollection<string>)c.ToList()));
modelBuilder.Entity<WorkEntity>()
.Property(e => e.Subjects)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null),
new ValueComparer<ICollection<string>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (ICollection<string>)c.ToList()));
modelBuilder.Entity<BookEntity>()
.Property(e => e.Publishers)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null),
new ValueComparer<ICollection<string>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (ICollection<string>)c.ToList()));
modelBuilder.Entity<BookEntity>()
.Property(e => e.Series)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null),
new ValueComparer<ICollection<string>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => (ICollection<string>)c.ToList()));
}
}

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyLibraryEntities\MyLibraryEntities.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,38 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyLibraryEntities
{
[Table("Authors")]
public class AuthorEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string? Bio { get; set; }
public ICollection<string>? AlternateNames { get; set; } = new List<string>();
public string SmallImage { get; set; }
public string MediumImage { get; set; }
public string LargeImage { get; set; }
public List<LinkEntity> Links { get; set; }
[Column("BirthDate", TypeName = "date")]
public DateTime? BirthDate { get; set; }
[Column("DeathDate", TypeName = "date")]
public DateTime? DeathDate { get; set; }
public List<WorkEntity> Works { get; } = new ();
public List<BookEntity> Books { get; } = new ();
}
}

@ -0,0 +1,23 @@
using System;
namespace MyLibraryEntities
{
public class BookEntity
{
public string Id { get; set; }
public string Title { get; set; }
public ICollection<string> Publishers { get; set; } = new List<string>();
public DateTime PublishDate { get; set; }
public string ISBN13 { get; set; }
public ICollection<string> Series { get; set; } = new List<string>();
public int NbPages { get; set; }
public string? Format { get; set; }
public Languages Language { get; set; }
public List<ContributorEntity> Contributors { get; set; }
public List<WorkEntity> Works { get; } = new ();
public List<AuthorEntity> Authors { get; } = new ();
public string SmallImage { get; set; }
public string MediumImage { get; set; }
public string LargeImage { get; set; }
}
}

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MyLibraryEntities;
public class ContributorEntity
{
public string Id { get; set; }
public string Name { get; set; }
public string Role { get; set; }
[ForeignKey("BookId")]
public BookEntity Book { get; set; }
public string BookId { get; set; }
}

@ -0,0 +1,11 @@
using System;
namespace MyLibraryEntities
{
public enum Languages
{
Unknown,
French,
English
}
}

@ -0,0 +1,18 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyLibraryEntities
{
public class LinkEntity
{
public string Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
[ForeignKey("AuthorId")]
public AuthorEntity Author { get; set; }
public string AuthorId { get; set; }
}
}

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

@ -0,0 +1,17 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyLibraryEntities
{
public class StringEntity
{
public string Id { get; set; }
public string Value { get; set; }
[ForeignKey("AuthorId")]
public AuthorEntity Author { get; set; }
public string AuthorId { get; set; }
}
}

@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyLibraryEntities
{
public class WorkEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
public string? Description { get; set; }
[Required]
public string Title { get; set; }
public ICollection<string>? Subjects { get; set; } = new List<string>();
public List<AuthorEntity> Authors { get; } = new ();
public List<BookEntity> Books { get; } = new ();
//public RatingsEntity Ratings { get; set; }
public float? RatingsAverage { get; set; }
public int? RatingsCount { get; set; }
}
}

@ -0,0 +1,84 @@
using System;
using LibraryDTO;
using MyLibraryEntities;
namespace MyLibraryManager
{
public static class Entity2DtoExtensions
{
public static LibraryDTO.Languages ToDto(this MyLibraryEntities.Languages lang)
{
return Enum.GetValues<LibraryDTO.Languages>().SingleOrDefault(v =>
Enum.GetName<LibraryDTO.Languages>(v) == Enum.GetName<MyLibraryEntities.Languages>(lang));
}
public static AuthorDTO ToDto(this AuthorEntity entity)
{
return new AuthorDTO
{
Id = entity.Id,
AlternateNames = entity.AlternateNames != null ? new List<string>(entity.AlternateNames.Where(an => an != null)) : new List<string>(),
Bio = entity.Bio,
BirthDate = entity.BirthDate,
DeathDate = entity.DeathDate,
Name = entity.Name,
Links = entity.Links != null ? new List<LinkDTO>(entity.Links.Where(l => l != null).ToDtos()) : new List<LinkDTO>(),
};
}
public static IEnumerable<AuthorDTO> ToDtos(this IEnumerable<AuthorEntity> entities)
=> entities.Select(a => a.ToDto());
public static LinkDTO ToDto(this LinkEntity entity)
=> new LinkDTO { Title = entity.Title, Url = entity.Url };
public static IEnumerable<LinkDTO> ToDtos(this IEnumerable<LinkEntity> entities)
=> entities.Select(l => l.ToDto());
public static WorkDTO ToDto(this WorkEntity entity)
{
return new WorkDTO
{
Id = entity.Id,
Description = entity.Description,
Ratings = entity.RatingsAverage != null ?
new RatingsDTO { Average = entity.RatingsAverage.Value, Count = entity.RatingsCount.Value } : null,
Subjects = new List<string>(entity.Subjects),
Title = entity.Title,
Authors = new List<AuthorDTO>(entity.Authors.ToDtos()),
};
}
public static IEnumerable<WorkDTO> ToDtos(this IEnumerable<WorkEntity> entities)
=> entities.Select(w => w.ToDto());
public static ContributorDTO ToDto(this ContributorEntity entity)
=> new ContributorDTO { Name = entity.Name, Role = entity.Role };
public static IEnumerable<ContributorDTO> ToDtos(this IEnumerable<ContributorEntity> entities)
=> entities.Select(l => l.ToDto());
public static BookDTO ToDto(this BookEntity entity)
{
return new BookDTO
{
Id = entity.Id,
Authors = entity.Authors != null ? new List<AuthorDTO>(entity.Authors.ToDtos()) : null,
Contributors = entity.Contributors != null ? new List<ContributorDTO>(entity.Contributors.ToDtos()) : null,
Format = entity.Format,
ISBN13 = entity.ISBN13,
Language = entity.Language.ToDto(),
NbPages = entity.NbPages,
PublishDate = entity.PublishDate,
Publishers = new List<string>(entity.Publishers),
Series = new List<string>(entity.Series),
Title = entity.Title,
Works = new List<WorkDTO>(entity.Works.ToDtos()),
};
}
public static IEnumerable<BookDTO> ToDtos(this IEnumerable<BookEntity> entities)
=> entities.Select(b => b.ToDto());
}
}

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DtoAbstractLayer\DtoAbstractLayer.csproj" />
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" />
<ProjectReference Include="..\MyLibraryDB\MyLibraryDB.csproj" />
<ProjectReference Include="..\MyLibraryEntities\MyLibraryEntities.csproj" />
<ProjectReference Include="..\StubbedDB\StubbedDB.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,124 @@
using System.Data.SqlTypes;
using DtoAbstractLayer;
using LibraryDTO;
using Microsoft.EntityFrameworkCore;
using MyLibraryDB;
using MyLibraryEntities;
using StubbedDB;
namespace MyLibraryManager;
public class MyLibraryMgr : IDtoManager
{
protected MyLibraryContext Context => _dbContext;
private readonly MyLibraryContext _dbContext;
public MyLibraryMgr() : this(new MyLibraryStubbedContext())
{
}
public MyLibraryMgr(string dbPlatformPath)
: this(new MyLibraryStubbedContext(dbPlatformPath))
{
Context.Database.EnsureCreated();
}
internal MyLibraryMgr(MyLibraryStubbedContext context)
{
_dbContext = context;
}
public async Task<AuthorDTO> GetAuthorById(string id)
{
var author = await Context.Authors.SingleOrDefaultAsync(a => a.Id.ToUpper().Contains(id.ToUpper()));
return author.ToDto();
}
public async Task<Tuple<long, IEnumerable<AuthorDTO>>> GetAuthorsByName(string substring, int index, int count, string sort = "")
{
var authors = Context.Authors.Where(a => a.Name.ToUpper().Contains(substring.ToUpper()));
switch(sort)
{
case "name":
authors = authors.OrderBy(a => a.Name);
break;
case "name_reverse":
authors = authors.OrderByDescending(a => a.Name);
break;
default:
break;
}
long nb = await authors.CountAsync();
return await Task.FromResult(Tuple.Create(nb, authors.Skip(index*count).Take(count).ToDtos()));
}
public async Task<BookDTO> GetBookById(string id)
{
var book = await Context.Books.SingleOrDefaultAsync(b => b.Id.ToUpper().Contains(id.ToUpper()));
return book.ToDto();
}
public async Task<BookDTO> GetBookByISBN(string isbn)
{
var book = await Context.Books.SingleOrDefaultAsync(b => b.ISBN13 == isbn);
return book?.ToDto();
}
Func<AuthorEntity, string, bool> predicateAuthorName =(a, s) => a.Name.ToUpper().Contains(s.ToUpper());
//|| a.AlternateNames.Count(an => an.ToUpper().Contains(s.ToUpper())) > 0;
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthor(string author, int index, int count, string sort = "")
{
var books2 = Context.Books.SelectMany(b => b.Authors, (b, a) => new {Book = b, AuthorName = a.Name });
var books3 = books2.Where(ba => ba.AuthorName.ToUpper().Contains(author.ToUpper()));
var books4 = books3.Select(ba => ba.Book).Distinct();
// var books = Context.Books.Where(b => b.Authors.Where(a => a.Name.ToUpper().Contains(author.ToUpper())).Count() > 0);// Exists(a => predicateAuthorName(a, author)));
return await SortAndFilterBooks(books4, index, count, sort);
}
private async Task<Tuple<long, IEnumerable<BookDTO>>> SortAndFilterBooks(IQueryable<BookEntity>? books, int index, int count, string sort = "")
{
switch(sort)
{
case "title":
books = books.OrderBy(a => a.Title);
break;
case "title_reverse":
books = books.OrderByDescending(a => a.Title);
break;
case "new":
books = books.OrderByDescending(a => a.PublishDate);
break;
case "old":
books = books.OrderBy(a => a.PublishDate);
break;
default:
break;
}
long nb = await books.CountAsync();
return await Task.FromResult(Tuple.Create(nb, books.Skip(index*count).Take(count).ToDtos()));
}
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "")
{
//var books = Context.Books.Where(b => b.Authors.Count(a => a.Id == authorId) > 0);
var books2 = Context.Books.SelectMany(b => b.Authors, (b, a) => new {Book = b, AuthorId = a.Id });
var books3 = books2.Where(ba => ba.AuthorId.ToUpper().Contains(authorId.ToUpper()));
var books4 = books3.Select(ba => ba.Book).Distinct();
return await SortAndFilterBooks(books4, index, count, sort);
}
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByTitle(string title, int index, int count, string sort = "")
{
var books = Context.Books.Where(b => b.Title.ToUpper().Contains(title.ToUpper()));
return await SortAndFilterBooks(books, index, count, sort);
}
}

@ -86,7 +86,7 @@ public class OpenLibClientAPI : IDtoManager
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "") public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "")
{ {
string searchedString = authorId.Trim().Replace(" ", "+"); string searchedString = authorId.Trim().Replace(" ", "+");
string route = $"{BasePath}{SearchAuthorPrefix}{searchedString}" string route = $"{BasePath}{SearchBookByAuthorPrefix}{searchedString}"
.AddPagination(index, count) .AddPagination(index, count)
.AddSort(sort); .AddSort(sort);
return await GetElement<Tuple<long, IEnumerable<BookDTO>>>(route, json => BookJsonReader.GetBooksByAuthor(json)); return await GetElement<Tuple<long, IEnumerable<BookDTO>>>(route, json => BookJsonReader.GetBooksByAuthor(json));

@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenLibraryWrapper", "OpenL
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0D260EDC-4F17-4BA4-9306-B6FEC6E5E394}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0D260EDC-4F17-4BA4-9306-B6FEC6E5E394}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenLibraryWrapper_UT", "OpenLibraryWrapper_UT\OpenLibraryWrapper_UT.csproj", "{B12BEDA7-EC41-417B-9C28-113A8ED87F35}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibraryDTO", "LibraryDTO\LibraryDTO.csproj", "{F59D46BA-6734-464E-8AC4-759BEFB87973}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibraryDTO", "LibraryDTO\LibraryDTO.csproj", "{F59D46BA-6734-464E-8AC4-759BEFB87973}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubbedDTO", "StubbedDTO\StubbedDTO.csproj", "{3CB3D741-DF5C-4C4A-82C2-5BFC212211AD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubbedDTO", "StubbedDTO\StubbedDTO.csproj", "{3CB3D741-DF5C-4C4A-82C2-5BFC212211AD}"
@ -21,6 +19,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenLibraryClient", "OpenLibraryClient\OpenLibraryClient.csproj", "{3A429457-D882-44E3-B65E-107554C2E91F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenLibraryClient", "OpenLibraryClient\OpenLibraryClient.csproj", "{3A429457-D882-44E3-B65E-107554C2E91F}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyLibraryEntities", "MyLibraryEntities\MyLibraryEntities.csproj", "{4E54987D-1790-49F9-9027-700A894B2BD9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyLibraryDB", "MyLibraryDB\MyLibraryDB.csproj", "{0ED941B5-E5C9-4A54-BAC8-4F7FB44F2947}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubbedDB", "StubbedDB\StubbedDB.csproj", "{951F7FDA-FA71-46CA-9DDE-4DF0AAAC9DA9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenLibraryWrapper_UT", "Tests\OpenLibraryWrapper_UT\OpenLibraryWrapper_UT.csproj", "{7FA060D6-3490-4FE3-808F-06370DF8A3B9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyLibraryDB_Tests", "Tests\MyLibraryDB_Tests\MyLibraryDB_Tests.csproj", "{A3137A3B-7BA6-4AC8-80C6-653F50079D55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyLibraryManager", "MyLibraryManager\MyLibraryManager.csproj", "{DA2B0562-31DA-4059-B402-404EC31874B6}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -31,10 +41,6 @@ Global
{EF0DED5C-7559-4D43-A30B-AE916FCDA078}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF0DED5C-7559-4D43-A30B-AE916FCDA078}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF0DED5C-7559-4D43-A30B-AE916FCDA078}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF0DED5C-7559-4D43-A30B-AE916FCDA078}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF0DED5C-7559-4D43-A30B-AE916FCDA078}.Release|Any CPU.Build.0 = Release|Any CPU {EF0DED5C-7559-4D43-A30B-AE916FCDA078}.Release|Any CPU.Build.0 = Release|Any CPU
{B12BEDA7-EC41-417B-9C28-113A8ED87F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B12BEDA7-EC41-417B-9C28-113A8ED87F35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B12BEDA7-EC41-417B-9C28-113A8ED87F35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B12BEDA7-EC41-417B-9C28-113A8ED87F35}.Release|Any CPU.Build.0 = Release|Any CPU
{F59D46BA-6734-464E-8AC4-759BEFB87973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F59D46BA-6734-464E-8AC4-759BEFB87973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F59D46BA-6734-464E-8AC4-759BEFB87973}.Debug|Any CPU.Build.0 = Debug|Any CPU {F59D46BA-6734-464E-8AC4-759BEFB87973}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F59D46BA-6734-464E-8AC4-759BEFB87973}.Release|Any CPU.ActiveCfg = Release|Any CPU {F59D46BA-6734-464E-8AC4-759BEFB87973}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -55,6 +61,30 @@ Global
{3A429457-D882-44E3-B65E-107554C2E91F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A429457-D882-44E3-B65E-107554C2E91F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A429457-D882-44E3-B65E-107554C2E91F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A429457-D882-44E3-B65E-107554C2E91F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A429457-D882-44E3-B65E-107554C2E91F}.Release|Any CPU.Build.0 = Release|Any CPU {3A429457-D882-44E3-B65E-107554C2E91F}.Release|Any CPU.Build.0 = Release|Any CPU
{4E54987D-1790-49F9-9027-700A894B2BD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E54987D-1790-49F9-9027-700A894B2BD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E54987D-1790-49F9-9027-700A894B2BD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E54987D-1790-49F9-9027-700A894B2BD9}.Release|Any CPU.Build.0 = Release|Any CPU
{0ED941B5-E5C9-4A54-BAC8-4F7FB44F2947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0ED941B5-E5C9-4A54-BAC8-4F7FB44F2947}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0ED941B5-E5C9-4A54-BAC8-4F7FB44F2947}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0ED941B5-E5C9-4A54-BAC8-4F7FB44F2947}.Release|Any CPU.Build.0 = Release|Any CPU
{951F7FDA-FA71-46CA-9DDE-4DF0AAAC9DA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{951F7FDA-FA71-46CA-9DDE-4DF0AAAC9DA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{951F7FDA-FA71-46CA-9DDE-4DF0AAAC9DA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{951F7FDA-FA71-46CA-9DDE-4DF0AAAC9DA9}.Release|Any CPU.Build.0 = Release|Any CPU
{7FA060D6-3490-4FE3-808F-06370DF8A3B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FA060D6-3490-4FE3-808F-06370DF8A3B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FA060D6-3490-4FE3-808F-06370DF8A3B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FA060D6-3490-4FE3-808F-06370DF8A3B9}.Release|Any CPU.Build.0 = Release|Any CPU
{A3137A3B-7BA6-4AC8-80C6-653F50079D55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3137A3B-7BA6-4AC8-80C6-653F50079D55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3137A3B-7BA6-4AC8-80C6-653F50079D55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3137A3B-7BA6-4AC8-80C6-653F50079D55}.Release|Any CPU.Build.0 = Release|Any CPU
{DA2B0562-31DA-4059-B402-404EC31874B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA2B0562-31DA-4059-B402-404EC31874B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA2B0562-31DA-4059-B402-404EC31874B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA2B0562-31DA-4059-B402-404EC31874B6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -63,6 +93,7 @@ Global
SolutionGuid = {2E40DC1C-BE57-48E8-B7C2-B9CFF589DB5B} SolutionGuid = {2E40DC1C-BE57-48E8-B7C2-B9CFF589DB5B}
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{B12BEDA7-EC41-417B-9C28-113A8ED87F35} = {0D260EDC-4F17-4BA4-9306-B6FEC6E5E394} {7FA060D6-3490-4FE3-808F-06370DF8A3B9} = {0D260EDC-4F17-4BA4-9306-B6FEC6E5E394}
{A3137A3B-7BA6-4AC8-80C6-653F50079D55} = {0D260EDC-4F17-4BA4-9306-B6FEC6E5E394}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

@ -31,6 +31,9 @@
<ProjectReference Include="..\OpenLibraryClient\OpenLibraryClient.csproj"> <ProjectReference Include="..\OpenLibraryClient\OpenLibraryClient.csproj">
<GlobalPropertiesToRemove></GlobalPropertiesToRemove> <GlobalPropertiesToRemove></GlobalPropertiesToRemove>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\MyLibraryManager\MyLibraryManager.csproj">
<GlobalPropertiesToRemove></GlobalPropertiesToRemove>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Program.cs"> <Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Program.cs">

@ -2,6 +2,7 @@
using DtoAbstractLayer; using DtoAbstractLayer;
using LibraryDTO; using LibraryDTO;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using MyLibraryManager;
using OpenLibraryClient; using OpenLibraryClient;
using StubbedDTO; using StubbedDTO;
@ -9,7 +10,7 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddSingleton<IDtoManager,OpenLibClientAPI>(); builder.Services.AddSingleton<IDtoManager, Stub>();
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
@ -23,4 +24,3 @@ app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();

@ -36,6 +36,14 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
},
"OpenLibraryWrapper": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:34198;http://localhost:46179",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
} }
} }
} }

@ -0,0 +1,115 @@
using System;
using StubbedDTO;
using System.Linq;
using LibraryDTO;
using MyLibraryEntities;
namespace StubbedDB
{
public static class Json2Data
{
public static MyLibraryEntities.Languages ToEntity(this LibraryDTO.Languages lang)
{
return Enum.GetValues<MyLibraryEntities.Languages>().SingleOrDefault(v =>
Enum.GetName<MyLibraryEntities.Languages>(v) == Enum.GetName<LibraryDTO.Languages>(lang));
}
public static IEnumerable<AuthorEntity> ToAuthorsData()
{
var temp = Stub.Authors.Select(a =>
new AuthorEntity
{
Id = a.Id,
Name = a.Name,
Bio = a.Bio,
BirthDate = a.BirthDate,
DeathDate = a.DeathDate,
SmallImage = $"https://covers.openlibrary.org/a/olid/{a.Id.Substring(a.Id.LastIndexOf("/"))}-S.jpg",
MediumImage = $"https://covers.openlibrary.org/a/olid/{a.Id.Substring(a.Id.LastIndexOf("/"))}-M.jpg",
LargeImage = $"https://covers.openlibrary.org/a/olid/{a.Id.Substring(a.Id.LastIndexOf("/"))}-L.jpg",
AlternateNames = a.AlternateNames
});
return temp;
}
public static IEnumerable<object> ToLinksData()
{
return Stub.Authors.Where(a => a.Links != null && a.Links.Count > 0)
.SelectMany(a => a.Links, (author,link) =>
new
{
Id = $"{author.Id.Substring(author.Id.LastIndexOf("/"))}-{author.Links.IndexOf(link)}",
Title = link.Title,
Url = link.Url,
AuthorId = author.Id
});
}
public static IEnumerable<object> ToWorksData()
{
return Stub.Works.Select(w =>
new
{
Id = w.Id,
Description = w.Description,
RatingsAverage = w.Ratings?.Average,
RatingsCount = w.Ratings?.Count,
Title = w.Title,
Subjects = w.Subjects
});
}
public static IEnumerable<object> ToAuthorsWorksData()
{
return Stub.Works.SelectMany(w => w.Authors,
(w, a) => new { AuthorsId = a.Id, WorksId = w.Id });
}
public static IEnumerable<object> ToBooksData()
{
return Stub.Books.Select(b =>
new
{
Publishers = b.Publishers,
Title = b.Title,
NbPages = b.NbPages,
ISBN13 = b.ISBN13,
Language = b.Language.ToEntity(),
PublishDate = b.PublishDate,
Id = b.Id,
Series = b.Series,
Format = b.Format,
SmallImage = $"https://covers.openlibrary.org/b/isbn/{b.ISBN13}-S.jpg",
MediumImage = $"https://covers.openlibrary.org/b/isbn/{b.ISBN13}-M.jpg",
LargeImage = $"https://covers.openlibrary.org/b/isbn/{b.ISBN13}-l.jpg"
});
}
public static IEnumerable<object> ToBooksWorksData()
{
return Stub.Books.SelectMany(b => b.Works,
(b, w) => new {BooksId = b.Id, WorksId = w.Id});
}
public static IEnumerable<object> ToContributorsData()
{
return Stub.Books.SelectMany( b => b.Contributors,
(b, c) => new
{
Id = $"{b.Id}-c{b.Contributors.IndexOf(c)}",
BookId = b.Id,
Name = c.Name,
Role = c.Role
});
}
public static IEnumerable<object> ToAuthorsBooksData()
{
var proj = (BookDTO b, AuthorDTO a) => new { AuthorsId = a.Id, BooksId = b.Id};
var collec1 = Stub.Books.SelectMany(b => b.Authors, proj);
var collec2 = Stub.Books.SelectMany(b => b.Works.SelectMany(w => w.Authors), proj);
return collec1.Union(collec2);
}
}
}

@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore;
using MyLibraryDB;
using MyLibraryEntities;
using static System.Reflection.Metadata.BlobBuilder;
namespace StubbedDB;
public class MyLibraryStubbedContext : MyLibraryContext
{
public MyLibraryStubbedContext() : base() { }
public MyLibraryStubbedContext(string dbPlatformPath)
:base(dbPlatformPath)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AuthorEntity>().HasData(
Json2Data.ToAuthorsData());
modelBuilder.Entity<LinkEntity>().HasData(
Json2Data.ToLinksData());
modelBuilder.Entity<WorkEntity>().HasData(
Json2Data.ToWorksData());
modelBuilder.Entity("AuthorEntityWorkEntity").HasData(
Json2Data.ToAuthorsWorksData());
modelBuilder.Entity<BookEntity>().HasData(
Json2Data.ToBooksData());
modelBuilder.Entity("BookEntityWorkEntity").HasData(
Json2Data.ToBooksWorksData());
modelBuilder.Entity<ContributorEntity>().HasData(
Json2Data.ToContributorsData());
modelBuilder.Entity("AuthorEntityBookEntity").HasData(
Json2Data.ToAuthorsBooksData());
}
}

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyLibraryDB\MyLibraryDB.csproj" />
<ProjectReference Include="..\StubbedDTO\StubbedDTO.csproj" />
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" />
</ItemGroup>
</Project>

@ -33,6 +33,7 @@ public class Stub : IDtoManager
using(StreamReader readerRatings = File.OpenText(ratingsFile)) using(StreamReader readerRatings = File.OpenText(ratingsFile))
{ {
var work = WorkJsonReader.ReadWork(reader.ReadToEnd(), readerRatings.ReadToEnd()); var work = WorkJsonReader.ReadWork(reader.ReadToEnd(), readerRatings.ReadToEnd());
if(work.Authors != null)
foreach(var author in work.Authors.ToList()) foreach(var author in work.Authors.ToList())
{ {
var newAuthor = Authors.SingleOrDefault(a => a.Id == author.Id); var newAuthor = Authors.SingleOrDefault(a => a.Id == author.Id);
@ -68,7 +69,7 @@ public class Stub : IDtoManager
public Task<AuthorDTO> GetAuthorById(string id) public Task<AuthorDTO> GetAuthorById(string id)
{ {
var author = Stub.Authors.SingleOrDefault(a => a.Id == id); var author = Stub.Authors.SingleOrDefault(a => a.Id.Contains(id));
return Task.FromResult(author); return Task.FromResult(author);
} }
@ -147,8 +148,8 @@ public class Stub : IDtoManager
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "") public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "")
{ {
var books = Stub.Books.Where(b => b.Authors.Exists(a => a.Id == authorId) var books = Stub.Books.Where(b => b.Authors.Exists(a => a.Id.Contains(authorId))
|| b.Works.Exists(w => w.Authors.Exists(a => a.Id == authorId))); || b.Works.Exists(w => w.Authors.Exists(a => a.Id.Contains(authorId))));
return await OrderBooks(books, index, count, sort); return await OrderBooks(books, index, count, sort);
} }
@ -162,13 +163,15 @@ public class Stub : IDtoManager
{ {
IEnumerable<AuthorDTO> authors = new List<AuthorDTO>(); IEnumerable<AuthorDTO> authors = new List<AuthorDTO>();
if(book.Authors != null) if(book.Authors != null && book.Authors.Count > 0)
{ {
authors = authors.Union(book.Authors); authors = authors.Union(book.Authors);
} }
if(book.Works != null) if(book.Works != null)
{ {
authors = authors.Union(book.Works.SelectMany(w => w.Authors)); var worksAuthors = book.Works.SelectMany(w => w.Authors).ToList();
if(worksAuthors.Count > 0)
authors = authors.Union(worksAuthors);
} }
foreach(var author in authors) foreach(var author in authors)
{ {

@ -6,11 +6,6 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" />
<ProjectReference Include="..\DtoAbstractLayer\DtoAbstractLayer.csproj" />
<ProjectReference Include="..\JsonReader\JsonReader.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="books\" /> <None Remove="books\" />
<None Remove="authors\" /> <None Remove="authors\" />
@ -29,4 +24,9 @@
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JsonReader\JsonReader.csproj" />
<ProjectReference Include="..\DtoAbstractLayer\DtoAbstractLayer.csproj" />
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" />
</ItemGroup>
</Project> </Project>

@ -1 +1,12 @@
{"name": "Michel Demuth", "personal_name": "Michel Demuth", "last_modified": {"type": "/type/datetime", "value": "2008-08-26 02:41:15.604911"}, "key": "/authors/OL1846639A", "type": {"key": "/type/author"}, "id": 6527877, "revision": 2} {
"name": "Michel Demuth",
"personal_name": "Michel Demuth",
"last_modified": {
"type": "/type/datetime",
"value": "2008-08-26 02:41:15.604911"
},
"key": "/authors/OL1846639A",
"type": { "key": "/type/author" },
"id": 6527877,
"revision": 2
}

@ -1 +1,34 @@
{"personal_name": "Dick, Philip K.", "source_records": ["amazon:8445007327", "bwb:9780722129562", "amazon:0792776232", "ia:pacificpark0000dick", "amazon:2277213799", "amazon:2266163019", "bwb:9798599263227", "amazon:1433276712", "ia:ejonescreoilmond0000dick", "amazon:6051719164", "amazon:6254493632", "amazon:2277117749", "amazon:1987781619", "amazon:1433248239", "amazon:1480594407"], "alternate_names": ["Philip Kindred Dick", "Philip Dick", "Philip Kendred Dick", "Philip K Dick"], "bio": "Philip Kindred Dick was an American novelist, short story writer, and essayist whose published work during his lifetime was almost entirely in the science fiction genre. Dick explored sociological, political and metaphysical themes in novels dominated by monopolistic corporations, authoritarian governments, and altered states. In his later works, Dick's thematic focus strongly reflected his personal interest in metaphysics and theology. He often drew upon his own life experiences and addressed the nature of drug abuse, paranoia and schizophrenia, and transcendental experiences in novels such as A Scanner Darkly and VALIS.\r\n\r\nSource and more information: [Wikipedia (EN)](http://en.wikipedia.org/wiki/Philip_K._Dick)", "type": {"key": "/type/author"}, "death_date": "2 March 1982", "remote_ids": {"isni": "0000000121251093", "wikidata": "Q171091", "viaf": "27063583"}, "name": "Philip K. Dick", "links": [{"title": "Wikipedia link to Philip K Dick", "url": "http://en.wikipedia.org/wiki/Philip_K._Dick", "type": {"key": "/type/link"}}], "photos": [6295259], "birth_date": "16 December 1928", "key": "/authors/OL274606A", "latest_revision": 23, "revision": 23, "created": {"type": "/type/datetime", "value": "2008-04-01T03:28:50.625462"}, "last_modified": {"type": "/type/datetime", "value": "2022-11-29T21:21:41.951561"}} {
"personal_name": "Dick, Philip K.",
"source_records": [ "amazon:8445007327", "bwb:9780722129562", "amazon:0792776232", "ia:pacificpark0000dick", "amazon:2277213799", "amazon:2266163019", "bwb:9798599263227", "amazon:1433276712", "ia:ejonescreoilmond0000dick", "amazon:6051719164", "amazon:6254493632", "amazon:2277117749", "amazon:1987781619", "amazon:1433248239", "amazon:1480594407" ],
"alternate_names": [ "Philip Kindred Dick", "Philip Dick", "Philip Kendred Dick", "Philip K Dick" ],
"bio": "Philip Kindred Dick was an American novelist, short story writer, and essayist whose published work during his lifetime was almost entirely in the science fiction genre. Dick explored sociological, political and metaphysical themes in novels dominated by monopolistic corporations, authoritarian governments, and altered states. In his later works, Dick's thematic focus strongly reflected his personal interest in metaphysics and theology. He often drew upon his own life experiences and addressed the nature of drug abuse, paranoia and schizophrenia, and transcendental experiences in novels such as A Scanner Darkly and VALIS.\r\n\r\nSource and more information: [Wikipedia (EN)](http://en.wikipedia.org/wiki/Philip_K._Dick)",
"type": { "key": "/type/author" },
"death_date": "2 March 1982",
"remote_ids": {
"isni": "0000000121251093",
"wikidata": "Q171091",
"viaf": "27063583"
},
"name": "Philip K. Dick",
"links": [
{
"title": "Wikipedia link to Philip K Dick",
"url": "http://en.wikipedia.org/wiki/Philip_K._Dick",
"type": { "key": "/type/link" }
}
],
"photos": [ 6295259 ],
"birth_date": "16 December 1928",
"key": "/authors/OL274606A",
"latest_revision": 23,
"revision": 23,
"created": {
"type": "/type/datetime",
"value": "2008-04-01T03:28:50.625462"
},
"last_modified": {
"type": "/type/datetime",
"value": "2022-11-29T21:21:41.951561"
}
}

@ -0,0 +1 @@
{"name": "Gilles Goullet", "last_modified": {"type": "/type/datetime", "value": "2008-04-30 08:14:56.482104"}, "key": "/authors/OL3113900A", "type": {"key": "/type/author"}, "id": 11970651, "revision": 1}

@ -1 +1,11 @@
{"name": "H\u00e9l\u00e8ne Collon", "last_modified": {"type": "/type/datetime", "value": "2008-04-30 08:14:56.482104"}, "key": "/authors/OL3113922A", "type": {"key": "/type/author"}, "id": 11970257, "revision": 1} {
"name": "H\u00e9l\u00e8ne Collon",
"last_modified": {
"type": "/type/datetime",
"value": "2008-04-30 08:14:56.482104"
},
"key": "/authors/OL3113922A",
"type": { "key": "/type/author" },
"id": 11970257,
"revision": 1
}

@ -1 +1,17 @@
{"name": "Alain Damasio", "key": "/authors/OL3980331A", "type": {"key": "/type/author"}, "remote_ids": {"wikidata": "Q2829704"}, "birth_date": "1 August 1969", "latest_revision": 2, "revision": 2, "created": {"type": "/type/datetime", "value": "2008-04-30T20:50:18.033121"}, "last_modified": {"type": "/type/datetime", "value": "2022-12-19T19:05:32.693708"}} {
"name": "Alain Damasio",
"key": "/authors/OL3980331A",
"type": { "key": "/type/author" },
"remote_ids": { "wikidata": "Q2829704" },
"birth_date": "1 August 1969",
"latest_revision": 2,
"revision": 2,
"created": {
"type": "/type/datetime",
"value": "2008-04-30T20:50:18.033121"
},
"last_modified": {
"type": "/type/datetime",
"value": "2022-12-19T19:05:32.693708"
}
}

@ -1 +1,36 @@
{"personal_name": "James S. A. Corey", "remote_ids": {"isni": "0000000382626033", "viaf": "266413968", "wikidata": "Q6142591"}, "source_records": ["amazon:1478933771", "amazon:1528822218", "amazon:1456121650", "bwb:9780356510385", "amazon:0678452547", "bwb:9780356517773"], "alternate_names": ["Daniel Abraham", "Ty Franck", "James S.A. Corey", "James James S. A. Corey"], "type": {"key": "/type/author"}, "key": "/authors/OL6982995A", "entity_type": "org", "links": [{"title": "Source", "url": "http://www.danielabraham.com/james-s-a-corey/", "type": {"key": "/type/link"}}], "bio": {"type": "/type/text", "value": "James S.A. Corey is the pen name used by collaborators [Daniel Abraham](https://openlibrary.org/authors/OL1427729A/Daniel_Abraham) and [Ty Franck](https://openlibrary.org/authors/OL7523472A/Ty_Franck).\r\n\r\nThe first and last name are taken from Abraham's and Franck's middle names, respectively, and S.A. are the initials of Abraham's daughter."}, "photos": [11112303], "name": "James S. A. Corey", "latest_revision": 13, "revision": 13, "created": {"type": "/type/datetime", "value": "2011-10-20T08:06:05.906616"}, "last_modified": {"type": "/type/datetime", "value": "2023-05-18T18:14:26.659278"}} {
"personal_name": "James S. A. Corey",
"remote_ids": {
"isni": "0000000382626033",
"viaf": "266413968",
"wikidata": "Q6142591"
},
"source_records": [ "amazon:1478933771", "amazon:1528822218", "amazon:1456121650", "bwb:9780356510385", "amazon:0678452547", "bwb:9780356517773" ],
"alternate_names": [ "Daniel Abraham", "Ty Franck", "James S.A. Corey", "James James S. A. Corey" ],
"type": { "key": "/type/author" },
"key": "/authors/OL6982995A",
"entity_type": "org",
"links": [
{
"title": "Source",
"url": "http://www.danielabraham.com/james-s-a-corey/",
"type": { "key": "/type/link" }
}
],
"bio": {
"type": "/type/text",
"value": "James S.A. Corey is the pen name used by collaborators [Daniel Abraham](https://openlibrary.org/authors/OL1427729A/Daniel_Abraham) and [Ty Franck](https://openlibrary.org/authors/OL7523472A/Ty_Franck).\r\n\r\nThe first and last name are taken from Abraham's and Franck's middle names, respectively, and S.A. are the initials of Abraham's daughter."
},
"photos": [ 11112303 ],
"name": "James S. A. Corey",
"latest_revision": 13,
"revision": 13,
"created": {
"type": "/type/datetime",
"value": "2011-10-20T08:06:05.906616"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-05-18T18:14:26.659278"
}
}

@ -1 +1,15 @@
{"key": "/authors/OL7475792A", "name": "Ada Palmer", "type": {"key": "/type/author"}, "latest_revision": 4, "revision": 4, "created": {"type": "/type/datetime", "value": "2019-03-11T19:38:25.579004"}, "last_modified": {"type": "/type/datetime", "value": "2021-12-07T07:11:29.213401"}} {
"key": "/authors/OL7475792A",
"name": "Ada Palmer",
"type": { "key": "/type/author" },
"latest_revision": 4,
"revision": 4,
"created": {
"type": "/type/datetime",
"value": "2019-03-11T19:38:25.579004"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-12-07T07:11:29.213401"
}
}

@ -0,0 +1 @@
{"name": "Robert Charles Wilson", "created": {"type": "/type/datetime", "value": "2020-05-08T21:00:30.785249"}, "last_modified": {"type": "/type/datetime", "value": "2020-05-08T21:00:30.785249"}, "latest_revision": 1, "key": "/authors/OL7876839A", "type": {"key": "/type/author"}, "revision": 1}

@ -0,0 +1,42 @@
{
"remote_ids": {
"viaf": "59083797",
"wikidata": "Q7934",
"isni": "0000000121347853"
},
"name": "Frank Herbert",
"source_records": [ "amazon:3870703903", "amazon:2253113190", "amazon:1427228493" ],
"alternate_names": [ "Herbert, Frank", "FRANK HERBERT", "Frank HERBERT", "herbert-frank", "frank herbert", "Herbert Frank", "Frank Herbert Dost Korpe", "HERBERT FRANK", "Franck Herbert", "F Herbert" ],
"photos": [ 12194537, 7277115, 10643754 ],
"links": [
{
"url": "http://en.wikipedia.org/wiki/Frank_Herbert",
"type": { "key": "/type/link" },
"title": "Wikipedia English"
},
{
"url": "http://fr.wikipedia.org/wiki/Frank_Herbert",
"type": { "key": "/type/link" },
"title": "Wikipedia France"
}
],
"key": "/authors/OL79034A",
"birth_date": "8 October 1920",
"death_date": "11 February 1986",
"type": { "key": "/type/author" },
"personal_name": "Herbert, Frank.",
"bio": {
"type": "/type/text",
"value": "Real name: Franklin Patrick Herbert Jr."
},
"latest_revision": 15,
"revision": 15,
"created": {
"type": "/type/datetime",
"value": "2008-04-01T03:28:50.625462"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-11-12T11:41:55.357817"
}
}

@ -1 +1,16 @@
{"type": {"key": "/type/author"}, "name": "Frank Herbert", "key": "/authors/OL9956442A", "source_records": ["amazon:2221252306"], "latest_revision": 1, "revision": 1, "created": {"type": "/type/datetime", "value": "2021-11-14T17:07:35.515652"}, "last_modified": {"type": "/type/datetime", "value": "2021-11-14T17:07:35.515652"}} {
"type": { "key": "/type/author" },
"name": "Frank Herbert",
"key": "/authors/OL9956442A",
"source_records": [ "amazon:2221252306" ],
"latest_revision": 1,
"revision": 1,
"created": {
"type": "/type/datetime",
"value": "2021-11-14T17:07:35.515652"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-11-14T17:07:35.515652"
}
}

@ -1 +1,24 @@
{"publishers": ["Actes Sud"], "title": "L'\u00c9veil du L\u00e9viathan", "number_of_pages": 624, "isbn_13": ["9782330033118"], "covers": [7412481], "languages": [{"key": "/languages/fre"}], "publish_date": "4 juin 2014", "key": "/books/OL25910297M", "publish_places": ["France"], "works": [{"key": "/works/OL17334140W"}], "type": {"key": "/type/edition"}, "source_records": ["amazon:2330033117"], "latest_revision": 5, "revision": 5, "created": {"type": "/type/datetime", "value": "2016-04-22T11:47:01.838591"}, "last_modified": {"type": "/type/datetime", "value": "2023-02-02T01:19:11.921173"}} {
"publishers": [ "Actes Sud" ],
"title": "L'\u00c9veil du L\u00e9viathan",
"number_of_pages": 624,
"isbn_13": [ "9782330033118" ],
"covers": [ 7412481 ],
"languages": [ { "key": "/languages/fre" } ],
"publish_date": "4 juin 2014",
"key": "/books/OL25910297M",
"publish_places": [ "France" ],
"works": [ { "key": "/works/OL17334140W" } ],
"type": { "key": "/type/edition" },
"source_records": [ "amazon:2330033117" ],
"latest_revision": 5,
"revision": 5,
"created": {
"type": "/type/datetime",
"value": "2016-04-22T11:47:01.838591"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-02-02T01:19:11.921173"
}
}

@ -0,0 +1,23 @@
{
"publishers": [ "Editions Gallimard" ],
"source_records": [ "amazon:2070793109" ],
"title": "La m\u00e9nagerie de papier",
"identifiers": { "amazon": [ "2070793109" ] },
"isbn_13": [ "9782070793105" ],
"covers": [ 8750266 ],
"created": {
"type": "/type/datetime",
"value": "2019-08-05T10:36:44.503432"
},
"physical_format": "mass market paperback",
"isbn_10": [ "2070793109" ],
"latest_revision": 1,
"key": "/books/OL27258011M",
"last_modified": {
"type": "/type/datetime",
"value": "2019-08-05T10:36:44.503432"
},
"works": [ { "key": "/works/OL20078005W" } ],
"type": { "key": "/type/edition" },
"revision": 1
}

@ -0,0 +1,38 @@
{
"publishers": [ "Le B\u00e9lial'" ],
"subtitle": "Terra Ignota volume 2",
"covers": [ 9376451 ],
"physical_format": "paperback",
"full_title": "Sept redditions : Terra Ignota volume 2",
"key": "/books/OL27989051M",
"authors": [ { "key": "/authors/OL7475792A" } ],
"source_records": [ "amazon:2843449626" ],
"title": "Sept redditions",
"notes": "Source title: Sept redditions: Terra Ignota volume 2 (Roman)",
"number_of_pages": 544,
"publish_date": "May 28, 2020",
"works": [ { "key": "/works/OL19213555W" } ],
"type": { "key": "/type/edition" },
"identifiers": {},
"isbn_10": [ "2843449626" ],
"isbn_13": [ "9782843449628" ],
"classifications": {},
"languages": [ { "key": "/languages/fre" } ],
"contributors": [
{
"name": "Michelle Charrier",
"role": "Translator"
}
],
"series": [ "Terra Ignota #2" ],
"latest_revision": 4,
"revision": 4,
"created": {
"type": "/type/datetime",
"value": "2020-05-02T22:25:18.922926"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-12-07T07:10:56.070304"
}
}

@ -0,0 +1 @@
{"publishers": ["GALLIMARD"], "last_modified": {"type": "/type/datetime", "value": "2020-06-28T05:17:49.626242"}, "source_records": ["amazon:2070469875"], "title": "La trilogie Spin", "notes": {"type": "/type/text", "value": "Source title: La trilogie Spin (Folio SF - XL) (French Edition)"}, "number_of_pages": 1120, "isbn_13": ["9782070469871"], "covers": [10218906], "created": {"type": "/type/datetime", "value": "2020-06-28T05:17:49.626242"}, "physical_format": "paperback", "isbn_10": ["2070469875"], "publish_date": "Jun 02, 2016", "key": "/books/OL28294024M", "authors": [{"key": "/authors/OL7876839A"}, {"key": "/authors/OL3113900A"}], "latest_revision": 1, "works": [{"key": "/works/OL20889233W"}], "type": {"key": "/type/edition"}, "revision": 1}

@ -0,0 +1,32 @@
{
"publishers": [ "Pocket", "POCKET" ],
"source_records": [ "amazon:2266235818" ],
"title": "Le Cycle de Dune Tome 2/Le Messie de Dune",
"notes": {
"type": "/type/text",
"value": "Source title: Le Cycle de Dune Tome 2/Le Messie de Dune (Science-fiction) (French Edition)"
},
"number_of_pages": 336,
"isbn_13": [ "9782266235815" ],
"covers": [ 10328275 ],
"physical_format": "mass market paperback",
"isbn_10": [ "2266235818" ],
"publish_date": "Nov 22, 2012",
"key": "/books/OL28639494M",
"authors": [
{ "key": "/authors/OL79034A" },
{ "key": "/authors/OL1846639A" }
],
"works": [ { "key": "/works/OL893526W" } ],
"type": { "key": "/type/edition" },
"latest_revision": 3,
"revision": 3,
"created": {
"type": "/type/datetime",
"value": "2020-08-05T18:59:33.402965"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-10-24T22:27:33.839388"
}
}

@ -0,0 +1,31 @@
{
"identifiers": { "wikidata": [ "Q81118625" ] },
"title": "Trop semblable \u00e0 l'\u00e9clair",
"publish_date": "2019",
"publishers": [ "Le B\u00e9lial'" ],
"type": { "key": "/type/edition" },
"isbn_13": [ "9782843449581" ],
"series": [ "Terra Ignota #1" ],
"physical_format": "paperback",
"contributors": [
{
"name": "Michelle Charrier",
"role": "Translator"
}
],
"languages": [ { "key": "/languages/fre" } ],
"key": "/books/OL35698073M",
"number_of_pages": 672,
"works": [ { "key": "/works/OL19800093W" } ],
"source_records": [ "amazon:2843449588" ],
"latest_revision": 4,
"revision": 4,
"created": {
"type": "/type/datetime",
"value": "2021-12-07T02:12:00.907076"
},
"last_modified": {
"type": "/type/datetime",
"value": "2022-11-15T15:47:17.497202"
}
}

@ -1 +1,32 @@
{"works": [{"key": "/works/OL19635836W"}], "title": "La Volont\u00e9 de se battre", "publishers": ["Le B\u00e9lial'"], "publish_date": "2020", "key": "/books/OL35698083M", "type": {"key": "/type/edition"}, "identifiers": {}, "covers": [12392970], "isbn_13": ["9782843449758"], "classifications": {}, "languages": [{"key": "/languages/fre"}], "contributors": [{"name": "Michelle Charrier", "role": "Translator"}], "number_of_pages": 526, "series": ["Terra Ignota #3"], "physical_format": "paperback", "latest_revision": 3, "revision": 3, "created": {"type": "/type/datetime", "value": "2021-12-07T02:23:07.593997"}, "last_modified": {"type": "/type/datetime", "value": "2021-12-07T02:24:57.135563"}} {
"works": [ { "key": "/works/OL19635836W" } ],
"title": "La Volont\u00e9 de se battre",
"publishers": [ "Le B\u00e9lial'" ],
"publish_date": "2020",
"key": "/books/OL35698083M",
"type": { "key": "/type/edition" },
"identifiers": {},
"covers": [ 12392970 ],
"isbn_13": [ "9782843449758" ],
"classifications": {},
"languages": [ { "key": "/languages/fre" } ],
"contributors": [
{
"name": "Michelle Charrier",
"role": "Translator"
}
],
"number_of_pages": 526,
"series": [ "Terra Ignota #3" ],
"physical_format": "paperback",
"latest_revision": 3,
"revision": 3,
"created": {
"type": "/type/datetime",
"value": "2021-12-07T02:23:07.593997"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-12-07T02:24:57.135563"
}
}

@ -1 +1,26 @@
{"type": {"key": "/type/edition"}, "title": "La Zone du Dehors", "authors": [{"key": "/authors/OL3980331A"}], "publish_date": "Feb 04, 2021", "source_records": ["amazon:2072927528"], "number_of_pages": 656, "publishers": ["FOLIO", "GALLIMARD"], "isbn_10": ["2072927528"], "isbn_13": ["9782072927522"], "physical_format": "pocket book", "full_title": "La Zone du Dehors", "covers": [12393645], "works": [{"key": "/works/OL19960903W"}], "key": "/books/OL35699439M", "latest_revision": 1, "revision": 1, "created": {"type": "/type/datetime", "value": "2021-12-07T22:26:13.534930"}, "last_modified": {"type": "/type/datetime", "value": "2021-12-07T22:26:13.534930"}} {
"type": { "key": "/type/edition" },
"title": "La Zone du Dehors",
"authors": [ { "key": "/authors/OL3980331A" } ],
"publish_date": "Feb 04, 2021",
"source_records": [ "amazon:2072927528" ],
"number_of_pages": 656,
"publishers": [ "FOLIO", "GALLIMARD" ],
"isbn_10": [ "2072927528" ],
"isbn_13": [ "9782072927522" ],
"physical_format": "pocket book",
"full_title": "La Zone du Dehors",
"covers": [ 12393645 ],
"works": [ { "key": "/works/OL19960903W" } ],
"key": "/books/OL35699439M",
"latest_revision": 1,
"revision": 1,
"created": {
"type": "/type/datetime",
"value": "2021-12-07T22:26:13.534930"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-12-07T22:26:13.534930"
}
}

@ -1 +1,31 @@
{"type": {"key": "/type/edition"}, "title": "Dune - tome 1", "authors": [{"key": "/authors/OL9956442A"}, {"key": "/authors/OL1846639A"}], "publish_date": "Nov 22, 2012", "source_records": ["amazon:2266233203"], "number_of_pages": 832, "publishers": ["POCKET", "Pocket"], "isbn_10": ["2266233203"], "isbn_13": ["9782266233200"], "physical_format": "pocket book", "notes": {"type": "/type/text", "value": "Source title: Dune - tome 1 (1)"}, "works": [{"key": "/works/OL27962193W"}], "key": "/books/OL38218739M", "latest_revision": 1, "revision": 1, "created": {"type": "/type/datetime", "value": "2022-05-30T17:18:00.228322"}, "last_modified": {"type": "/type/datetime", "value": "2022-05-30T17:18:00.228322"}} {
"type": { "key": "/type/edition" },
"title": "Dune - tome 1",
"authors": [
{ "key": "/authors/OL9956442A" },
{ "key": "/authors/OL1846639A" }
],
"publish_date": "Nov 22, 2012",
"source_records": [ "amazon:2266233203" ],
"number_of_pages": 832,
"publishers": [ "POCKET", "Pocket" ],
"isbn_10": [ "2266233203" ],
"isbn_13": [ "9782266233200" ],
"physical_format": "pocket book",
"notes": {
"type": "/type/text",
"value": "Source title: Dune - tome 1 (1)"
},
"works": [ { "key": "/works/OL27962193W" } ],
"key": "/books/OL38218739M",
"latest_revision": 1,
"revision": 1,
"created": {
"type": "/type/datetime",
"value": "2022-05-30T17:18:00.228322"
},
"last_modified": {
"type": "/type/datetime",
"value": "2022-05-30T17:18:00.228322"
}
}

@ -1 +1,28 @@
{"type": {"key": "/type/edition"}, "title": "Total Recall et autres r\u00e9cits", "authors": [{"key": "/authors/OL274606A"}, {"key": "/authors/OL3113922A"}], "publish_date": "Jul 12, 2012", "source_records": ["amazon:2070448908"], "number_of_pages": 448, "publishers": ["FOLIO", "GALLIMARD"], "isbn_10": ["2070448908"], "isbn_13": ["9782070448906"], "physical_format": "pocket book", "works": [{"key": "/works/OL28185064W"}], "key": "/books/OL38586212M", "covers": [13858141], "latest_revision": 3, "revision": 3, "created": {"type": "/type/datetime", "value": "2022-07-10T01:29:29.296699"}, "last_modified": {"type": "/type/datetime", "value": "2023-04-07T22:44:13.567567"}} {
"type": { "key": "/type/edition" },
"title": "Total Recall et autres r\u00e9cits",
"authors": [
{ "key": "/authors/OL274606A" },
{ "key": "/authors/OL3113922A" }
],
"publish_date": "Jul 12, 2012",
"source_records": [ "amazon:2070448908" ],
"number_of_pages": 448,
"publishers": [ "FOLIO", "GALLIMARD" ],
"isbn_10": [ "2070448908" ],
"isbn_13": [ "9782070448906" ],
"physical_format": "pocket book",
"works": [ { "key": "/works/OL28185064W" } ],
"key": "/books/OL38586212M",
"covers": [ 13858141 ],
"latest_revision": 3,
"revision": 3,
"created": {
"type": "/type/datetime",
"value": "2022-07-10T01:29:29.296699"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-04-07T22:44:13.567567"
}
}

@ -1 +1,13 @@
{"summary": {"average": null, "count": 0}, "counts": {"1": 0, "2": 0, "3": 0, "4": 0, "5": 0}} {
"summary": {
"average": null,
"count": 0
},
"counts": {
"1": 0,
"2": 0,
"3": 0,
"4": 0,
"5": 0
}
}

@ -0,0 +1 @@
{"summary": {"average": 4.333333333333333, "count": 6, "sortable": 3.114900291576932}, "counts": {"1": 0, "2": 0, "3": 1, "4": 2, "5": 3}}

@ -1 +1,14 @@
{"summary": {"average": 4.8, "count": 5, "sortable": 3.216059213089321}, "counts": {"1": 0, "2": 0, "3": 0, "4": 1, "5": 4}} {
"summary": {
"average": 4.8,
"count": 5,
"sortable": 3.216059213089321
},
"counts": {
"1": 0,
"2": 0,
"3": 0,
"4": 1,
"5": 4
}
}

@ -0,0 +1 @@
{"summary": {"average": 3.6923076923076925, "count": 13, "sortable": 2.9048164811987554}, "counts": {"1": 2, "2": 2, "3": 1, "4": 1, "5": 7}}

@ -1 +1,14 @@
{"summary": {"average": 4.0, "count": 1, "sortable": 2.3286737413641063}, "counts": {"1": 0, "2": 0, "3": 0, "4": 1, "5": 0}} {
"summary": {
"average": 4.0,
"count": 1,
"sortable": 2.3286737413641063
},
"counts": {
"1": 0,
"2": 0,
"3": 0,
"4": 1,
"5": 0
}
}

@ -0,0 +1 @@
{"summary": {"average": null, "count": 0}, "counts": {"1": 0, "2": 0, "3": 0, "4": 0, "5": 0}}

@ -0,0 +1 @@
{"summary": {"average": null, "count": 0}, "counts": {"1": 0, "2": 0, "3": 0, "4": 0, "5": 0}}

@ -1 +1,14 @@
{"summary": {"average": 3.0, "count": 1, "sortable": 2.19488243981746}, "counts": {"1": 0, "2": 0, "3": 1, "4": 0, "5": 0}} {
"summary": {
"average": 3.0,
"count": 1,
"sortable": 2.19488243981746
},
"counts": {
"1": 0,
"2": 0,
"3": 1,
"4": 0,
"5": 0
}
}

@ -1 +1,13 @@
{"summary": {"average": null, "count": 0}, "counts": {"1": 0, "2": 0, "3": 0, "4": 0, "5": 0}} {
"summary": {
"average": null,
"count": 0
},
"counts": {
"1": 0,
"2": 0,
"3": 0,
"4": 0,
"5": 0
}
}

@ -0,0 +1 @@
{"summary": {"average": 3.9368421052631577, "count": 95, "sortable": 3.721132697535656}, "counts": {"1": 3, "2": 4, "3": 19, "4": 39, "5": 30}}

@ -1 +1,22 @@
{"title": "L'\u00c9veil du L\u00e9viathan", "key": "/works/OL17334140W", "authors": [{"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL6982995A"}}], "type": {"key": "/type/work"}, "covers": [7412481], "latest_revision": 3, "revision": 3, "created": {"type": "/type/datetime", "value": "2016-04-22T11:47:01.838591"}, "last_modified": {"type": "/type/datetime", "value": "2023-02-02T01:19:11.921173"}} {
"title": "L'\u00c9veil du L\u00e9viathan",
"key": "/works/OL17334140W",
"authors": [
{
"type": { "key": "/type/author_role" },
"author": { "key": "/authors/OL6982995A" }
}
],
"type": { "key": "/type/work" },
"covers": [ 7412481 ],
"latest_revision": 3,
"revision": 3,
"created": {
"type": "/type/datetime",
"value": "2016-04-22T11:47:01.838591"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-02-02T01:19:11.921173"
}
}

@ -0,0 +1 @@
{"description": "\"It is a world in which near-instantaneous travel from continent to continent is free to all. In which automation now provides for everybody's basic needs. In which nobody living can remember an actual war. In which it is illegal for three or more people to gather for the practice of religion--but ecumenical \"sensayers\" minister in private, one-on-one. In which gendered language is archaic, and to dress as strongly male or female is, if not exactly illegal, deeply taboo. In which nationality is a fading memory, and most people identify instead with their choice of the seven global Hives, distinguished from one another by their different approaches to the big questions of life. And it is a world in which, unknown to most, the entire social order is teetering on the edge of collapse. Because even in utopia, humans will conspire. And also because something new has arisen: Bridger, the child who can bring inanimate objects to conscious life\"--", "covers": [8429595, 8772829, 9338105, 9376451], "key": "/works/OL19213555W", "authors": [{"author": {"key": "/authors/OL7475792A"}, "type": {"key": "/type/author_role"}}], "title": "Seven Surrenders", "subjects": ["Utopias", "Fiction", "Fiction, science fiction, general", "series:terra_ignota"], "type": {"key": "/type/work"}, "latest_revision": 6, "revision": 6, "created": {"type": "/type/datetime", "value": "2019-03-11T19:38:25.579004"}, "last_modified": {"type": "/type/datetime", "value": "2021-12-07T07:10:56.070304"}}

@ -1 +1,24 @@
{"description": "\"The long years of near-utopia have come to an abrupt end. Peace and order are now figments of the past. Corruption, deception, and insurgency hum within the once steadfast leadership of the Hives, nations without fixed location. The heartbreaking truth is that for decades, even centuries, the leaders of the great Hives bought the world's stability with a trickle of secret murders, mathematically planned. So that no faction could ever dominate. So that the balance held. The Hives' fa\u00e7ade of solidity is the only hope they have for maintaining a semblance of order, for preventing the public from succumbing to the savagery and bloodlust of wars past. But as the great secret becomes more and more widely known, that fa\u00e7ade is slipping away. Just days earlier, the world was a pinnacle of human civilization. Now everyone--Hives and hiveless, Utopians and sensayers, emperors and the downtrodden, warriors and saints--scrambles to prepare for the seemingly inevitable war\"--", "covers": [8544084, 8619055, 10180814], "key": "/works/OL19635836W", "authors": [{"author": {"key": "/authors/OL7475792A"}, "type": {"key": "/type/author_role"}}], "title": "The Will to Battle", "subjects": ["Utopias", "Fiction", "Fiction, science fiction, general", "series:terra_ignota"], "type": {"key": "/type/work"}, "latest_revision": 7, "revision": 7, "created": {"type": "/type/datetime", "value": "2019-04-21T08:07:12.674468"}, "last_modified": {"type": "/type/datetime", "value": "2021-12-07T07:08:28.885088"}} {
"description": "\"The long years of near-utopia have come to an abrupt end. Peace and order are now figments of the past. Corruption, deception, and insurgency hum within the once steadfast leadership of the Hives, nations without fixed location. The heartbreaking truth is that for decades, even centuries, the leaders of the great Hives bought the world's stability with a trickle of secret murders, mathematically planned. So that no faction could ever dominate. So that the balance held. The Hives' fa\u00e7ade of solidity is the only hope they have for maintaining a semblance of order, for preventing the public from succumbing to the savagery and bloodlust of wars past. But as the great secret becomes more and more widely known, that fa\u00e7ade is slipping away. Just days earlier, the world was a pinnacle of human civilization. Now everyone--Hives and hiveless, Utopians and sensayers, emperors and the downtrodden, warriors and saints--scrambles to prepare for the seemingly inevitable war\"--",
"covers": [ 8544084, 8619055, 10180814 ],
"key": "/works/OL19635836W",
"authors": [
{
"author": { "key": "/authors/OL7475792A" },
"type": { "key": "/type/author_role" }
}
],
"title": "The Will to Battle",
"subjects": [ "Utopias", "Fiction", "Fiction, science fiction, general", "series:terra_ignota" ],
"type": { "key": "/type/work" },
"latest_revision": 7,
"revision": 7,
"created": {
"type": "/type/datetime",
"value": "2019-04-21T08:07:12.674468"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-12-07T07:08:28.885088"
}
}

@ -0,0 +1 @@
{"description": "\"The world into which Mycroft and Carlyle have been born is as strange to our 21st-century eyes as ours would be to a native of the 1500s. It is a hard-won utopia built on technologically-generated abundance, and also on complex and mandatory systems of labeling all public writing and speech... And in this world, Mycroft and Carlyle have stumbled on the wild card that may destabilize the system: the boy Bridger, who can effortlessly make his wishes come true. Who can, it would seem, bring inanimate objects to life...\"--Book jacket.", "covers": [8600692, 8673756], "key": "/works/OL19800093W", "authors": [{"author": {"key": "/authors/OL7475792A"}, "type": {"key": "/type/author_role"}}], "title": "Too Like the Lightning", "subjects": ["Utopias", "Prisoners", "Twenty-fifth century", "Fiction", "Third millennium", "Fiction, science fiction, general", "series:terra_ignota", "Spirituality", "Magic", "FICTION", "Science Fiction"], "type": {"key": "/type/work"}, "latest_revision": 6, "revision": 6, "created": {"type": "/type/datetime", "value": "2019-06-23T20:50:30.749828"}, "last_modified": {"type": "/type/datetime", "value": "2022-03-12T06:35:56.153258"}}

@ -1 +1,22 @@
{"title": "La zone du dehors", "key": "/works/OL19960903W", "authors": [{"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL3980331A"}}], "type": {"key": "/type/work"}, "covers": [13472433], "latest_revision": 2, "revision": 2, "created": {"type": "/type/datetime", "value": "2019-07-17T23:01:16.580404"}, "last_modified": {"type": "/type/datetime", "value": "2023-03-15T21:59:10.897047"}} {
"title": "La zone du dehors",
"key": "/works/OL19960903W",
"authors": [
{
"type": { "key": "/type/author_role" },
"author": { "key": "/authors/OL3980331A" }
}
],
"type": { "key": "/type/work" },
"covers": [ 13472433 ],
"latest_revision": 2,
"revision": 2,
"created": {
"type": "/type/datetime",
"value": "2019-07-17T23:01:16.580404"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-03-15T21:59:10.897047"
}
}

@ -0,0 +1,16 @@
{
"title": "La m\u00e9nagerie de papier",
"created": {
"type": "/type/datetime",
"value": "2019-08-05T10:36:44.503432"
},
"covers": [ 8750266 ],
"last_modified": {
"type": "/type/datetime",
"value": "2019-08-05T10:36:44.503432"
},
"latest_revision": 1,
"key": "/works/OL20078005W",
"type": { "key": "/type/work" },
"revision": 1
}

@ -0,0 +1 @@
{"title": "La trilogie Spin", "created": {"type": "/type/datetime", "value": "2020-06-28T05:17:49.626242"}, "covers": [10218906], "last_modified": {"type": "/type/datetime", "value": "2020-06-28T05:17:49.626242"}, "latest_revision": 1, "key": "/works/OL20889233W", "authors": [{"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL7876839A"}}, {"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL3113900A"}}], "type": {"key": "/type/work"}, "revision": 1}

@ -1 +1,26 @@
{"type": {"key": "/type/work"}, "title": "Dune - tome 1", "authors": [{"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL9956442A"}}, {"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL1846639A"}}], "key": "/works/OL27962193W", "covers": [13823878], "latest_revision": 2, "revision": 2, "created": {"type": "/type/datetime", "value": "2022-05-30T17:18:00.228322"}, "last_modified": {"type": "/type/datetime", "value": "2023-04-04T09:05:11.531979"}} {
"type": { "key": "/type/work" },
"title": "Dune - tome 1",
"authors": [
{
"type": { "key": "/type/author_role" },
"author": { "key": "/authors/OL9956442A" }
},
{
"type": { "key": "/type/author_role" },
"author": { "key": "/authors/OL1846639A" }
}
],
"key": "/works/OL27962193W",
"covers": [ 13823878 ],
"latest_revision": 2,
"revision": 2,
"created": {
"type": "/type/datetime",
"value": "2022-05-30T17:18:00.228322"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-04-04T09:05:11.531979"
}
}

@ -1 +1,26 @@
{"type": {"key": "/type/work"}, "title": "Total Recall et autres r\u00e9cits", "authors": [{"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL274606A"}}, {"type": {"key": "/type/author_role"}, "author": {"key": "/authors/OL3113922A"}}], "key": "/works/OL28185064W", "covers": [13858141], "latest_revision": 3, "revision": 3, "created": {"type": "/type/datetime", "value": "2022-07-10T01:29:29.296699"}, "last_modified": {"type": "/type/datetime", "value": "2023-04-07T22:44:13.567567"}} {
"type": { "key": "/type/work" },
"title": "Total Recall et autres r\u00e9cits",
"authors": [
{
"type": { "key": "/type/author_role" },
"author": { "key": "/authors/OL274606A" }
},
{
"type": { "key": "/type/author_role" },
"author": { "key": "/authors/OL3113922A" }
}
],
"key": "/works/OL28185064W",
"covers": [ 13858141 ],
"latest_revision": 3,
"revision": 3,
"created": {
"type": "/type/datetime",
"value": "2022-07-10T01:29:29.296699"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-04-07T22:44:13.567567"
}
}

@ -0,0 +1 @@
{"subjects": ["American Science fiction", "Dune (Imaginary place)", "Fiction", "Fiction in English", "Imaginary places", "Science Fiction", "Translations into Russian", "Novela", "Dune (Lugar imaginario)", "Lectures et morceaux choisis", "Anglais (langue)", "Roman ame ricain", "Science-fiction ame ricaine", "Long Now Manual for Civilization", "Roman ame\u0301ricain", "Science-fiction ame\u0301ricaine", "Dune (imaginary place), fiction", "Fiction, science fiction, general"], "key": "/works/OL893526W", "title": "Dune Messiah", "authors": [{"author": {"key": "/authors/OL79034A"}, "type": {"key": "/type/author_role"}}, {"author": {"key": "/authors/OL1846639A"}, "type": {"key": "/type/author_role"}}], "type": {"key": "/type/work"}, "covers": [2421405, 1009421, 5277575, 284503, 8450071, 8771464, 10328275, 11422534, 11290920, 10849793, 979507, 8376882], "description": {"type": "/type/text", "value": "Bearbeitete Neuausgabe"}, "latest_revision": 18, "revision": 18, "created": {"type": "/type/datetime", "value": "2009-12-09T06:52:50.872607"}, "last_modified": {"type": "/type/datetime", "value": "2023-01-31T17:11:49.536060"}}

@ -0,0 +1,125 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\MyLibraryDB\MyLibraryDB.csproj" />
<ProjectReference Include="..\..\MyLibraryEntities\MyLibraryEntities.csproj" />
<ProjectReference Include="..\..\StubbedDB\StubbedDB.csproj" />
<ProjectReference Include="..\..\StubbedDTO\StubbedDTO.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Remove="authors\" />
<None Remove="books\" />
<None Remove="ratings\" />
<None Remove="works\" />
</ItemGroup>
<ItemGroup>
<None Update="authors\OL1846639A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="authors\OL274606A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="authors\OL3113922A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="authors\OL3980331A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="authors\OL6982995A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="authors\OL7475792A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="authors\OL9956442A.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="books\OL25910297M.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="books\OL35698083M.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="books\OL35699439M.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="books\OL38218739M.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="books\OL38586212M.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="ratings\OL17334140W.ratings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="ratings\OL19635836W.ratings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="ratings\OL19960903W.ratings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="ratings\OL27962193W.ratings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="ratings\OL28185064W.ratings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="works\OL17334140W.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="works\OL19635836W.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="works\OL19960903W.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="works\OL27962193W.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="works\OL28185064W.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="authors\" />
<Folder Include="books\" />
<Folder Include="ratings\" />
<Folder Include="works\" />
</ItemGroup>
</Project>

@ -0,0 +1,19 @@
// See https://aka.ms/new-console-template for more information
using Microsoft.EntityFrameworkCore;
using MyLibraryDB;
using StubbedDB;
//StubbedDTO.Stub.BasePath = Directory.GetCurrentDirectory();
using (var context = new MyLibraryStubbedContext())
{
foreach(var a in context.Authors.Include(auth => auth.Works))
{
Console.WriteLine($"{a.Id} - {a.Name} (Works count: {a.Works.Count})");
}
foreach(var b in context.Books.Include(book => book.Authors))
{
Console.WriteLine($"{b.Id} - {b.Title} (Authors count: {b.Authors.Count})");
}
}

@ -75,6 +75,6 @@ public class BookController_UT
long nbBooks = booksTupple.Item1; long nbBooks = booksTupple.Item1;
var books = booksTupple.Item2; var books = booksTupple.Item2;
Assert.Equal(2, books.Count()); Assert.Equal(4, books.Count());
} }
} }

@ -26,10 +26,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenLibraryWrapper\OpenLibraryWrapper.csproj" /> <ProjectReference Include="..\..\StubbedDTO\StubbedDTO.csproj" />
<ProjectReference Include="..\StubbedDTO\StubbedDTO.csproj" /> <ProjectReference Include="..\..\DtoAbstractLayer\DtoAbstractLayer.csproj" />
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" /> <ProjectReference Include="..\..\LibraryDTO\LibraryDTO.csproj" />
<ProjectReference Include="..\DtoAbstractLayer\DtoAbstractLayer.csproj" /> <ProjectReference Include="..\..\OpenLibraryClient\OpenLibraryClient.csproj" />
<ProjectReference Include="..\OpenLibraryClient\OpenLibraryClient.csproj" /> <ProjectReference Include="..\..\OpenLibraryWrapper\OpenLibraryWrapper.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
Loading…
Cancel
Save