Ajout des projets manquants et affichage ou non du menu d’ajout de livres

master
Lucas Evard 2 years ago
parent ee2b2bf2dc
commit 6b94c23a2f

BIN
.DS_Store vendored

Binary file not shown.

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

@ -0,0 +1,97 @@
using LibraryDTO;
namespace DtoAbstractLayer;
/// <summary>
/// abstract layer for requests on Books Library
/// </summary>
public interface IDtoManager
{
/// <summary>
/// get a book by specifying its id
/// </summary>
/// <param name="id">id of the Book to get</param>
/// <returns>a Book with this id (or null if id is unknown)</returns>
Task<BookDTO> GetBookById(string id);
/// <summary>
/// get a book by specifying its isbn
/// </summary>
/// <param name="isbn">isbn of the Book to get</param>
/// <returns>a Book with this isbn (or null if isbn is unknown)</returns>
Task<BookDTO> GetBookByISBN(string isbn);
/// <summary>
/// get books containing a substring in their titles
/// </summary>
/// <param name="title">the substring to look for in book titles</param>
/// <param name="index">index of the page of resulting books</param>
/// <param name="count">number of resulting books per page</param>
/// <param name="sort">sort criterium (not mandatory):
/// <ul>
/// <li>```title```: sort books by titles in alphabetical order,</li>
/// <li>```title_reverse```: sort books by titles in reverse alphabetical order,</li>
/// <li>```new```: sort books by publishing dates, beginning with the most recents,</li>
/// <li>```old```: sort books by publishing dates, beginning with the oldest</li>
/// </ul>
/// </param>
/// <returns>max <i>count</i> books</returns>
Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByTitle(string title, int index, int count, string sort = "");
/// <summary>
/// get books of a particular author by giving the author id
/// </summary>
/// <param name="authorId">the id of the author</param>
/// <param name="index">index of the page of resulting books</param>
/// <param name="count">number of resulting books per page</param>
/// <param name="sort">sort criterium (not mandatory):
/// <ul>
/// <li>```title```: sort books by titles in alphabetical order,</li>
/// <li>```title_reverse```: sort books by titles in reverse alphabetical order,</li>
/// <li>```new```: sort books by publishing dates, beginning with the most recents,</li>
/// <li>```old```: sort books by publishing dates, beginning with the oldest</li>
/// </ul>
/// </param>
/// <returns>max <i>count</i> books</returns>
Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "");
/// <summary>
/// get books of authors whose name (or alternate names) contains a particular substring
/// </summary>
/// <param name="author">name to look for in author names or alternate names</param>
/// <param name="index">index of the page of resulting books</param>
/// <param name="count">number of resulting books per page</param>
/// <param name="sort">sort criterium (not mandatory):
/// <ul>
/// <li>```title```: sort books by titles in alphabetical order,</li>
/// <li>```title_reverse```: sort books by titles in reverse alphabetical order,</li>
/// <li>```new```: sort books by publishing dates, beginning with the most recents,</li>
/// <li>```old```: sort books by publishing dates, beginning with the oldest</li>
/// </ul>
/// </param>
/// <returns>max <i>count</i> books</returns>
Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthor(string author, int index, int count, string sort = "");
/// <summary>
/// get an author by specifying its id
/// </summary>
/// <param name="id">id of the Author to get</param>
/// <returns>an author with this id (or null if id is unknown)</returns>
Task<AuthorDTO> GetAuthorById(string id);
/// <summary>
/// get authors containing a substring in their names (or alternate names)
/// </summary>
/// <param name="substring">the substring to look for in author names (or alternate names)</param>
/// <param name="index">index of the page of resulting authors</param>
/// <param name="count">number of resulting authors per page</param>
/// <param name="sort">sort criterium (not mandatory):
/// <ul>
/// <li>```name```: sort authors by names in alphabetical order,</li>
/// <li>```name_reverse```: sort authors by names in reverse alphabetical order,</li>
/// </ul>
/// </param>
/// <returns>max <i>count</i> authors</returns>
Task<Tuple<long, IEnumerable<AuthorDTO>>> GetAuthorsByName(string substring, int index, int count, string sort = "");
}

@ -0,0 +1,7 @@
namespace ExtensionsAndMappers;
public class Class1
{
}

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

@ -0,0 +1,80 @@
using System;
using LibraryDTO;
using Newtonsoft.Json.Linq;
using System.Globalization;
namespace JsonReader
{
public static class AuthorJsonReader
{
public static AuthorDTO ReadAuthor(string json)
{
JObject o = JObject.Parse(json);
string bioTokenAsString = null;
if(o.TryGetValue("bio", out JToken? bioToken))
{
if(bioToken.Type == JTokenType.String)
{
bioTokenAsString = (string)bioToken;
}
else
{
var bioTokenValue = o["bio"]?["value"];
bioTokenAsString = (string)bioTokenValue;
}
}
AuthorDTO author = new AuthorDTO
{
Id = (string)o["key"],
Name = (string)o["name"],
Bio = bioTokenAsString,
BirthDate = o.TryGetValue("birth_date", out JToken? bd) ? ReadDate((string)o["birth_date"]) : 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>(),
AlternateNames = o.TryGetValue("alternate_names", out JToken? altNames) ? altNames.Select(alt => (string)alt).ToList() : new List<string?>()
};
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)
{
JObject o = JObject.Parse(json);
long numFound = (long)o["numFound"];
var authors = o["docs"].Select(doc => new AuthorDTO
{
Id = $"/authors/{(string)doc["key"]}",
Name = (string)doc["name"],
});
return Tuple.Create(numFound, authors);
}
}
}

@ -0,0 +1,97 @@
using System.Globalization;
using LibraryDTO;
using Newtonsoft.Json.Linq;
namespace JsonReader;
public static class BookJsonReader
{
static Dictionary<string, Languages> languages = new Dictionary<string, Languages>()
{
[@"/languages/fre"] = Languages.French,
[@"/languages/eng"] = Languages.English,
["fre"] = Languages.French,
["eng"] = Languages.English,
[""] = Languages.Unknown
};
public static BookDTO ReadBook(string json)
{
JObject o = JObject.Parse(json);
var l = o["languages"]?.FirstOrDefault("");
Languages lang = l != null ? languages[(string)l["key"]] : Languages.Unknown;
//List<Tuple<string, CultureInfo>> pubDateFormat =new List<Tuple<string, CultureInfo>>()
//{
// Tuple.Create("d MMMM yyyy", CultureInfo.GetCultureInfo("fr-FR")),
// 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
{
Id = (string)o["key"],
Title = (string)o["title"],
Publishers = o["publishers"].Select(p => (string)p).ToList(),
PublishDate = publishDate.GetValueOrDefault(DateTime.Now),
ISBN13 = (string)o["isbn_13"][0],
NbPages = o["number_of_pages"] != null ? (int)o["number_of_pages"] : -1,
Language = lang,
Format = o.TryGetValue("physical_format", out JToken? f) ? (string)f : null,
Works = o["works"].Select(w => new WorkDTO { Id = (string)w["key"] }).ToList(),
Contributors = o.TryGetValue("contributors", out JToken? contr) ? contr.Select(c => new ContributorDTO { Name = (string)c["name"], Role = (string)c["role"] }).ToList() : new List<ContributorDTO>(),
Authors = o["authors"]?.Select(a => new AuthorDTO { Id = (string)a["key"] }).ToList()
};
if(book.Authors == null)
{
book.Authors = new List<AuthorDTO>();
}
return book;
}
public static Tuple<long, IEnumerable<BookDTO>> GetBooksByAuthor(string json)
{
JObject o = JObject.Parse(json);
long numFound = (long)o["numFound"];
var books = o["docs"].Select(doc => new BookDTO
{
Id = (string)(doc["seed"].First()),
Title = (string)doc["title"],
ISBN13 = (string)(doc["isbn"].First()),
Authors = doc["seed"].Where(s => ((string)s).StartsWith("/authors/"))
.Select(s => new AuthorDTO { Id = (string)s }).ToList(),
Language = languages.GetValueOrDefault((string)(doc["language"].First()))
});
return Tuple.Create(numFound, books);
}
public static Tuple<long, IEnumerable<BookDTO>> GetBooksByTitle(string json)
{
JObject o = JObject.Parse(json);
long numFound = (long)o["numFound"];
var books = o["docs"].Select(doc => new BookDTO
{
Id = (string)(doc["seed"].First()),
Title = (string)doc["title"],
ISBN13 = (string)(doc["isbn"].First()),
Authors = doc["seed"].Where(s => ((string)s).StartsWith("/authors/"))
.Select(s => new AuthorDTO { Id = (string)s }).ToList(),
Language = languages.GetValueOrDefault((string)(doc["language"].First()))
});
return Tuple.Create(numFound, books);
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,55 @@
using System;
using LibraryDTO;
using Newtonsoft.Json.Linq;
using System.Globalization;
namespace JsonReader
{
public static class WorkJsonReader
{
public static WorkDTO ReadWork(string json, string ratingsJson)
{
JObject o = JObject.Parse(json);
JObject r = JObject.Parse(ratingsJson);
var ratingsDto = new RatingsDTO();
if(r["summary"]["average"].Type != JTokenType.Float)
{
ratingsDto.Average = -1;
ratingsDto.Count = 0;
}
else
{
ratingsDto.Average = (float)r["summary"]["average"];
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
{
Id = (string)o["key"],
Title = (string)o["title"],
Authors = o.TryGetValue("authors", out JToken? authors) ? authors.Select(a => new AuthorDTO { Id = (string)a["author"]["key"] }).ToList() : new List<AuthorDTO>(),
Description = description,
Subjects = o.TryGetValue("subjects", out JToken? subjects) ? subjects.Select(s => (string)s).ToList() : new List<string>(),
Ratings = ratingsDto
};
return work;
}
}
}

@ -0,0 +1,18 @@
using System;
namespace LibraryDTO
{
public class AuthorDTO
{
public string Id { get; set; }
public string Name { get; set; }
public string ImageSmall => $"https://covers.openlibrary.org/a/olid/{Id.Substring(Id.LastIndexOf("/"))}-S.jpg";
public string ImageMedium => $"https://covers.openlibrary.org/a/olid/{Id.Substring(Id.LastIndexOf("/"))}-M.jpg";
public string ImageLarge => $"https://covers.openlibrary.org/a/olid/{Id.Substring(Id.LastIndexOf("/"))}-L.jpg";
public string Bio { get; set; }
public List<string> AlternateNames { get; set; } = new List<string>();
public List<LinkDTO> Links { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime? DeathDate { get; set; }
}
}

@ -0,0 +1,23 @@
using System;
namespace LibraryDTO
{
public class BookDTO
{
public string Id { get; set; }
public string Title { get; set; }
public List<string> Publishers { get; set; } = new List<string>();
public DateTime PublishDate { get; set; }
public string ISBN13 { get; set; }
public List<string> Series { get; set; } = new List<string>();
public int NbPages { get; set; }
public string Format { get; set; }
public Languages Language { get; set; }
public List<ContributorDTO> Contributors { get; set; }
public string ImageSmall => $"https://covers.openlibrary.org/b/isbn/{ISBN13}-S.jpg";
public string ImageMedium => $"https://covers.openlibrary.org/b/isbn/{ISBN13}-M.jpg";
public string ImageLarge => $"https://covers.openlibrary.org/b/isbn/{ISBN13}-L.jpg";
public List<WorkDTO> Works { get; set; } = new List<WorkDTO>();
public List<AuthorDTO> Authors { get; set; } = new List<AuthorDTO>();
}
}

@ -0,0 +1,10 @@
using System;
namespace LibraryDTO
{
public class ContributorDTO
{
public string Name { get; set; }
public string Role { get; set; }
}
}

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

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\net7.0\LibraryDTO.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\net7.0\LibraryDTO.xml</DocumentationFile>
</PropertyGroup>
</Project>

@ -0,0 +1,10 @@
using System;
namespace LibraryDTO
{
public class LinkDTO
{
public string Title { get; set; }
public string Url { get; set; }
}
}

@ -0,0 +1,10 @@
using System;
namespace LibraryDTO
{
public class RatingsDTO
{
public float Average { get; set; }
public int Count { get; set; }
}
}

@ -0,0 +1,14 @@
using System;
namespace LibraryDTO
{
public class WorkDTO
{
public string Id { get; set; }
public string Description { get; set; }
public string Title { get; set; }
public List<string> Subjects { get; set; } = new List<string>();
public List<AuthorDTO> Authors { get; set; } = new List<AuthorDTO>();
public RatingsDTO Ratings { get; set; }
}
}

@ -0,0 +1,32 @@
using System;
namespace Model
{
public class Author : IEquatable<Author>
{
public string Id { get; set; }
public string Name { get; set; }
public string ImageSmall => $"https://covers.openlibrary.org/a/olid/{Id.Substring(Id.LastIndexOf("/"))}-S.jpg";
public string ImageMedium => $"https://covers.openlibrary.org/a/olid/{Id.Substring(Id.LastIndexOf("/"))}-M.jpg";
public string ImageLarge => $"https://covers.openlibrary.org/a/olid/{Id.Substring(Id.LastIndexOf("/"))}-L.jpg";
public string Bio { get; set; }
public List<string> AlternateNames { get; set; } = new List<string>();
public List<Link> Links { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime? DeathDate { get; set; }
public bool Equals(Author? other)
=> Id == other.Id;
public override bool Equals(object? obj)
{
if(ReferenceEquals(obj, null)) return false;
if(ReferenceEquals(this, obj)) return true;
if(GetType() != obj.GetType()) return false;
return Equals(obj as Author);
}
public override int GetHashCode()
=> Id.GetHashCode();
}
}

@ -0,0 +1,41 @@
using static Model.Book;
namespace Model;
public class Book : IEquatable<Book>
{
public string Id { get; set; }
public string Title { get; set; }
public List<string> Publishers { get; set; } = new List<string>();
public DateTime PublishDate { get; set; }
public string ISBN13 { get; set; }
public List<string> Series { get; set; } = new List<string>();
public int NbPages { get; set; }
public string Format { get; set; }
public Languages Language { get; set; }
public List<Contributor> Contributors { get; set; }
public string ImageSmall => $"https://covers.openlibrary.org/b/isbn/{ISBN13}-S.jpg";
public string ImageMedium => $"https://covers.openlibrary.org/b/isbn/{ISBN13}-M.jpg";
public string ImageLarge => $"https://covers.openlibrary.org/b/isbn/{ISBN13}-L.jpg";
public List<Work> Works { get; set; } = new List<Work>();
public List<Author> Authors { get; set; } = new List<Author>();
public Status Status { get; set; }
public List<string> UserTags { get; set; } = new List<string>();
public float? UserRating { get; set; }
public string UserNote { get; set; }
public bool Equals(Book? other)
=> Id == other.Id;
public override bool Equals(object? obj)
{
if(ReferenceEquals(obj, null)) return false;
if(ReferenceEquals(this, obj)) return true;
if(GetType() != obj.GetType()) return false;
return Equals(obj as Book);
}
public override int GetHashCode()
=> Id.GetHashCode();
}

@ -0,0 +1,29 @@
using System;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Model
{
public class Borrowing : IEquatable<Borrowing> //emprunt
{
public string Id { get; set; }
public Book Book { get; set; }
public Contact Owner { get; set; }
public DateTime BorrowedAt { get; set; }
public DateTime? ReturnedAt { get; set; }
public bool Equals(Borrowing? other)
=> Id == other.Id;
public override bool Equals(object? obj)
{
if(ReferenceEquals(obj, null)) return false;
if(ReferenceEquals(this, obj)) return true;
if(GetType() != obj.GetType()) return false;
return Equals(obj as Borrowing);
}
public override int GetHashCode()
=> Id.GetHashCode();
}
}

@ -0,0 +1,25 @@
using System;
namespace Model
{
public class Contact : IEquatable<Contact>
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Equals(Contact? other)
=> Id == other.Id;
public override bool Equals(object? obj)
{
if(ReferenceEquals(obj, null)) return false;
if(ReferenceEquals(this, obj)) return true;
if(GetType() != obj.GetType()) return false;
return Equals(obj as Contact);
}
public override int GetHashCode()
=> Id.GetHashCode();
}
}

@ -0,0 +1,10 @@
using System;
namespace Model
{
public class Contributor
{
public string Name { get; set; }
public string Role { get; set; }
}
}

@ -0,0 +1,15 @@
using System;
namespace Model
{
public interface ILibraryManager
{
Task<Book> GetBookById(string id);
Task<Book> GetBookByISBN(string isbn);
Task<Tuple<long, IEnumerable<Book>>> GetBooksByTitle(string title, int index, int count, string sort = "");
Task<Tuple<long, IEnumerable<Book>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "");
Task<Tuple<long, IEnumerable<Book>>> GetBooksByAuthor(string author, int index, int count, string sort = "");
Task<Author> GetAuthorById(string id);
Task<Tuple<long, IEnumerable<Author>>> GetAuthorsByName(string substring, int index, int count, string sort = "");
}
}

@ -0,0 +1,40 @@
using System;
namespace Model
{
public interface IUserLibraryManager : ILibraryManager
{
Task<Tuple<long, IEnumerable<Book>>> GetBooksFromCollection(int index, int count, string sort = "");
Task<Book> AddBook(Book book);
Task<Book> AddBook(string id);
Task<Book> AddBookByIsbn(string isbn);
Task<bool> RemoveBook(Book book);
Task<bool> RemoveBook(string id);
Task<bool> RemoveBookByIsbn(string isbn);
Task<bool> AddToFavorites(Book book);
Task<bool> AddToFavorites(string bookId);
Task<bool> RemoveFromFavorites(Book book);
Task<bool> RemoveFromFavorites(string bookId);
Task<Book> UpdateBook(Book updatedBook);
Task<Contact> AddContact(Contact contact);
Task<bool> RemoveContact(Contact contact);
Task<bool> LendBook(Book book, Contact contact, DateTime? loanDate);
Task<bool> GetBackBook(Book book, DateTime? returnedDate);
Task<bool> BorrowBook(Book book, Contact owner, DateTime? borrowedDate);
Task<bool> GiveBackBook(Book book, DateTime? returnedDate);
Task<Tuple<long, IEnumerable<Loan>>> GetCurrentLoans(int index, int count);
Task<Tuple<long, IEnumerable<Loan>>> GetPastLoans(int index, int count);
Task<Tuple<long, IEnumerable<Borrowing>>> GetCurrentBorrowings(int index, int count);
Task<Tuple<long, IEnumerable<Borrowing>>> GetPastBorrowings(int index, int count);
Task<Tuple<long, IEnumerable<Contact>>> GetContacts(int index, int count);
}
}

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

@ -0,0 +1,10 @@
using System;
namespace Model
{
public class Link
{
public string Title { get; set; }
public string Url { get; set; }
}
}

@ -0,0 +1,27 @@
using System;
namespace Model
{
public class Loan : IEquatable<Loan> //prêt
{
public string Id { get; set; }
public Book Book { get; set; }
public Contact Loaner { get; set; }
public DateTime LoanedAt { get; set; }
public DateTime? ReturnedAt { get; set; }
public bool Equals(Loan? other)
=> Id == other.Id;
public override bool Equals(object? obj)
{
if(ReferenceEquals(obj, null)) return false;
if(ReferenceEquals(this, obj)) return true;
if(GetType() != obj.GetType()) return false;
return Equals(obj as Loan);
}
public override int GetHashCode()
=> Id.GetHashCode();
}
}

@ -0,0 +1,106 @@
using System;
using System.Collections.ObjectModel;
using System.Reflection;
namespace Model
{
public class Manager
{
private ILibraryManager LibraryManager { get; set; }
private IUserLibraryManager UserLibraryManager { get; set; }
public ReadOnlyCollection<Book> Books { get; private set; }
private List<Book> books = new();
public Manager(ILibraryManager libMgr, IUserLibraryManager userLibMgr)
{
LibraryManager = libMgr;
UserLibraryManager = userLibMgr;
Books = new ReadOnlyCollection<Book>(books);
}
public async Task<Book> GetBookById(string id)
=> await LibraryManager.GetBookById(id);
public async Task<Book> GetBookByISBN(string isbn)
=> await LibraryManager.GetBookByISBN(isbn);
public async Task<(long count, IEnumerable<Book> books)> GetBooksByTitle(string title, int index, int count, string sort = "")
{
var result = await LibraryManager.GetBooksByTitle(title, index, count, sort);
return (result.Item1, result.Item2);
}
public async Task<(long count, IEnumerable<Book> books)> GetBooksByAuthorId(string authorId, int index, int count, string sort = "")
{
var result = await LibraryManager.GetBooksByAuthorId(authorId, index, count, sort);
return (result.Item1, result.Item2);
}
public async Task<(long count, IEnumerable<Book> books)> GetBooksByAuthor(string author, int index, int count, string sort = "")
{
var result = await LibraryManager.GetBooksByAuthor(author, index, count, sort);
return (result.Item1, result.Item2);
}
public async Task<Author> GetAuthorById(string id)
=> await LibraryManager.GetAuthorById(id);
public async Task<(long count, IEnumerable<Author> authors)> GetAuthorsByName(string substring, int index, int count, string sort = "")
{
var result = await LibraryManager.GetAuthorsByName(substring, index, count, sort);
return (result.Item1, result.Item2);
}
public Task<Book> AddBookToCollection(string id)
{
return UserLibraryManager.AddBook(id);
}
public async Task<Book> GetBookByIdFromCollection(string id)
=> await UserLibraryManager.GetBookById(id);
public Task<Book> UpdateBook(Book book)
{
return UserLibraryManager.UpdateBook(book);
}
public Task<(long count, IEnumerable<Book> books)> GetBooksFromCollection(int index, int count, string sort = "")
{
var result = UserLibraryManager.GetBooksFromCollection(index, count, sort).Result;
return Task.FromResult((result.Item1, result.Item2));
}
public Task<(long count, IEnumerable<Contact> contacts)> GetContacts(int index, int count)
{
var result = UserLibraryManager.GetContacts(index, count).Result;
return Task.FromResult((result.Item1, result.Item2));
}
public Task<(long count, IEnumerable<Loan> loans)> GetCurrentLoans(int index, int count)
{
var result = UserLibraryManager.GetCurrentLoans(index, count).Result;
return Task.FromResult((result.Item1, result.Item2));
}
public Task<(long count, IEnumerable<Loan> loans)> GetPastLoans(int index, int count)
{
var result = UserLibraryManager.GetPastLoans(index, count).Result;
return Task.FromResult((result.Item1, result.Item2));
}
public Task<(long count, IEnumerable<Borrowing> borrowings)> GetCurrentBorrowings(int index, int count)
{
var result = UserLibraryManager.GetCurrentBorrowings(index, count).Result;
return Task.FromResult((result.Item1, result.Item2));
}
public Task<(long count, IEnumerable<Borrowing> borrowings)> GetPastBorrowings(int index, int count)
{
var result = UserLibraryManager.GetPastBorrowings(index, count).Result;
return Task.FromResult((result.Item1, result.Item2));
}
}
}

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

@ -0,0 +1,10 @@
using System;
namespace Model
{
public class Ratings
{
public float Average { get; set; }
public int Count { get; set; }
}
}

@ -0,0 +1,13 @@
using System;
namespace Model
{
public enum Status
{
Unknown,
Finished,
Reading,
NotRead,
ToBeRead
}
}

@ -0,0 +1,28 @@
using System;
namespace Model
{
public class Work : IEquatable<Work>
{
public string Id { get; set; }
public string Description { get; set; }
public string Title { get; set; }
public List<string> Subjects { get; set; } = new List<string>();
public List<Author> Authors { get; set; } = new List<Author>();
public Ratings Ratings { get; set; }
public bool Equals(Work? other)
=> Id == other.Id;
public override bool Equals(object? obj)
{
if(ReferenceEquals(obj, null)) return false;
if(ReferenceEquals(this, obj)) return true;
if(GetType() != obj.GetType()) return false;
return Equals(obj as Work);
}
public override int GetHashCode()
=> Id.GetHashCode();
}
}

@ -1,6 +1,7 @@
using System;
using System.Windows.Input;
using System.Threading.Tasks;
using System.Reflection;
namespace PocketBook.Applicative_VM
{
@ -9,42 +10,18 @@ namespace PocketBook.Applicative_VM
public ICommand Navigateto { get; private set; }
public NavigatorVM()
{
Navigateto = new Command<string>(async (parameter) =>
Navigateto = new Command(async (name) =>
{
if (!string.IsNullOrEmpty(parameter))
{
switch (parameter)
Type pageType = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(Page).IsAssignableFrom(t) && t.Name == name as String)
.FirstOrDefault();
if (pageType != null)
{
case "TousPage":
await App.Current.MainPage.Navigation.PushAsync(new TousPage());
break;
case "SharePage":
await App.Current.MainPage.Navigation.PushAsync(new SharePage());
break;
case "Auteur":
await App.Current.MainPage.Navigation.PushAsync(new Auteur());
break;
case "BookDetail":
await App.Current.MainPage.Navigation.PushAsync(new BookDetail());
break;
case "DatePublic":
await App.Current.MainPage.Navigation.PushAsync(new DatePublic());
break;
case "LaterPage":
await App.Current.MainPage.Navigation.PushAsync(new LaterPage());
break;
case "MarkPage":
await App.Current.MainPage.Navigation.PushAsync(new MarkPage());
break;
case "StatutPage":
await App.Current.MainPage.Navigation.PushAsync(new StatutPage());
break;
case "LikePage":
await App.Current.MainPage.Navigation.PushAsync(new LikePage());
break;
}
Page page = (Page)Activator.CreateInstance(pageType,this);
await App.Current.MainPage.Navigation.PushAsync(page);
}
else await App.Current.MainPage.Navigation.PopToRootAsync();
});
}
}

@ -0,0 +1,43 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace PocketBook.Applicative_VM
{
public class ScanMenuVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand ShowScanMenu { get; private set; }
private bool scanMenuIsVisible;
public bool ScanMenuIsVisible
{
get { return scanMenuIsVisible; }
set
{
if (scanMenuIsVisible != value)
{
scanMenuIsVisible = value;
OnPropertyChanged(nameof(ScanMenuIsVisible));
}
}
}
public ScanMenuVM()
{
ShowScanMenu = new Command(() =>
{
ScanMenuIsVisible = !ScanMenuIsVisible;
});
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

@ -40,7 +40,17 @@ public static class MauiProgram
});
builder.Services
.AddSingleton<NavigatorVM>()
.AddSingleton<ScanMenuVM>()
.AddSingleton<MainPage>()
.AddSingleton<TousPage>()
.AddSingleton<BookDetail>()
.AddSingleton<DatePublic>()
.AddSingleton<LikePage>()
.AddSingleton<LaterPage>()
.AddSingleton<MarkPage>()
.AddSingleton<SharePage>()
.AddSingleton<StatutPage>()
.AddSingleton<Auteur>()
;
#if DEBUG
builder.Logging.AddDebug();

@ -10,9 +10,6 @@
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" VerticalOptions="Start" Padding="5,10,0,10">
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="RetourMainMenu" />
</StackLayout.GestureRecognizers>
<Image Source="chevron_left.png">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}" />

@ -1,14 +1,15 @@
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class Auteur : ContentPage
{
public Auteur()
public NavigatorVM NavigateCommandBooks { get; private set; }
public Auteur(NavigatorVM navigator)
{
NavigateCommandBooks = navigator;
InitializeComponent();
}
private async void RetourMainMenu(object sender, EventArgs e)
{
await Navigation.PopToRootAsync();
}
}

@ -1,9 +1,15 @@
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class BookDetail : ContentPage
{
public BookDetail()
public NavigatorVM NavigateCommandBooks { get; private set; }
public BookDetail(NavigatorVM navigator)
{
NavigateCommandBooks = navigator;
InitializeComponent();
titreBook.Text = "La horde du contrevent";
bookAuteur.Text = "Alain Damasio";
@ -14,9 +20,4 @@ public partial class BookDetail : ContentPage
bookStatus.Text = "Non lu";
bookAdd.Text = "15 août 2013";
}
private async void RetourMainMenu(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
}

@ -2,15 +2,18 @@
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PocketBook.ContentViewBook"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:local="clr-namespace:PocketBook">
xmlns:local="clr-namespace:PocketBook"
x:Name="root">
<ContentView.GestureRecognizers>
<TapGestureRecognizer CommandParameter="{Binding CommandParameter, Source={x:Reference root}}" Command="{Binding Command, Source={x:Reference root}}"/>
</ContentView.GestureRecognizers>
<VerticalStackLayout Margin="15,0,0,0">
<StackLayout Orientation="Horizontal" Margin="0,5,0,0">
<Image x:Name="bookImage" Margin="0,0,10,0" WidthRequest="75" HeightRequest="110"/>
<Image x:Name="bookImage" Source="{Binding BookImage, Source={x:Reference root}}" Margin="0,0,10,0" WidthRequest="75" HeightRequest="110"/>
<StackLayout Orientation="Vertical">
<Label x:Name="bookName" FontFamily="Pro-Text-Bold" FontAttributes="Bold"/>
<Label x:Name="autorName" FontFamily="Pro-Text-Semibold" FontSize="12"/>
<Label x:Name="bookStatus" FontFamily="Pro-Text-Semibold" FontSize="12" TextColor="#AFAFAF"/>
<Label x:Name="bookName" Text="{Binding BookName, Source={x:Reference root}}" FontFamily="Pro-Text-Bold" FontAttributes="Bold"/>
<Label x:Name="autorName" Text="{Binding AutorName, Source={x:Reference root}}" FontFamily="Pro-Text-Semibold" FontSize="12"/>
<Label x:Name="bookStatus" Text="{Binding BookStatus, Source={x:Reference root}}" FontFamily="Pro-Text-Semibold" FontSize="12" TextColor="#AFAFAF"/>
<StackLayout Orientation="Horizontal" Margin="0,30,0,0">
<local:Stars NbStars="1" Margin="0,0,0,5"/>
</StackLayout>

@ -1,3 +1,5 @@
using System.Windows.Input;
namespace PocketBook;
public partial class ContentViewBook : ContentView
{
@ -14,6 +16,24 @@ public partial class ContentViewBook : ContentView
public static readonly BindableProperty BookImageProperty =
BindableProperty.Create(nameof(BookImage), typeof(string), typeof(ContentViewBook), string.Empty);
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ContentViewBook));
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.Create(nameof(CommandParameter), typeof(string), typeof(ContentViewBook));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public string CommandParameter
{
get { return (string)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public string BookName
{
get { return (string)GetValue(BookNameProperty); }
@ -38,9 +58,5 @@ public partial class ContentViewBook : ContentView
public ContentViewBook()
{
InitializeComponent();
bookName.SetBinding(Label.TextProperty, new Binding(nameof(BookName), source: this));
autorName.SetBinding(Label.TextProperty, new Binding(nameof(AutorName), source: this));
bookStatus.SetBinding(Label.TextProperty, new Binding(nameof(BookStatus), source: this));
bookImage.SetBinding(Image.SourceProperty, new Binding(nameof(BookImage), source: this));
}
}

@ -2,8 +2,11 @@
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PocketBook.ScanMenuView"
x:Name="root"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">
<Border BackgroundColor="{AppThemeBinding Light={StaticResource scanMenuBackgroundLight}, Dark={StaticResource scanMenuBackgroundDark}}"
Stroke="{AppThemeBinding Light={StaticResource scanMenuDividerLight}, Dark={StaticResource scanMenuDividerDark}}"
WidthRequest="258">

@ -8,9 +8,6 @@
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" VerticalOptions="Start" Padding="5,10,0,10">
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="RetourMainMenu"/>
</StackLayout.GestureRecognizers>
<Image Source="chevron_left.png">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}"/>

@ -1,14 +1,15 @@
namespace PocketBook;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class DatePublic : ContentPage
{
public DatePublic()
public NavigatorVM NavigateCommandBooks { get; private set; }
public DatePublic(NavigatorVM navigator)
{
NavigateCommandBooks = navigator;
InitializeComponent();
}
private async void RetourMainMenu(object sender, EventArgs e)
{
await Navigation.PopToRootAsync();
}
}

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PocketBook.LaterPage"
@ -10,9 +10,6 @@
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" Padding="5,10,0,10" >
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="RetourMainMenu"/>
</StackLayout.GestureRecognizers>
<Image Source="chevron_left.png">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}"/>

@ -1,17 +1,15 @@
using CommunityToolkit.Maui.Views;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class LaterPage : ContentPage
{
public LaterPage()
{
InitializeComponent();
}
public NavigatorVM NavigateCommandBooks { get; private set; }
private async void RetourMainMenu(object sender, EventArgs e)
public LaterPage(NavigatorVM navigator)
{
await Navigation.PopToRootAsync();
NavigateCommandBooks = navigator;
InitializeComponent();
}
}

@ -10,9 +10,6 @@
<VerticalStackLayout>
<StackLayout Orientation="Horizontal" Padding="5,10,0,10" >
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="RetourMainMenu"/>
</StackLayout.GestureRecognizers>
<Image Source="chevron_left.png">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}"/>

@ -1,18 +1,16 @@
using CommunityToolkit.Maui.Views;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class LikePage : ContentPage
{
public LikePage()
{
InitializeComponent();
}
public NavigatorVM NavigateCommandBooks { get; private set; }
private async void RetourMainMenu(object sender, EventArgs e)
public LikePage(NavigatorVM navigator)
{
await Navigation.PopToRootAsync();
NavigateCommandBooks = navigator;
InitializeComponent();
}
}

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PocketBook.MarkPage"
@ -9,9 +9,6 @@
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" VerticalOptions="Start" Padding="5,10,0,10">
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="RetourMainMenu"/>
</StackLayout.GestureRecognizers>
<Image Source="chevron_left.png">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}"/>

@ -1,17 +1,15 @@
using CommunityToolkit.Maui.Views;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class MarkPage : ContentPage
{
public MarkPage()
{
InitializeComponent();
}
public NavigatorVM NavigateCommandBooks { get; private set; }
private async void RetourMainMenu(object sender, EventArgs e)
public MarkPage(NavigatorVM navigator)
{
await Navigation.PopToRootAsync();
NavigateCommandBooks = navigator;
InitializeComponent();
}
}

@ -1,16 +1,15 @@
using CommunityToolkit.Maui.Views;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class SharePage : ContentPage
{
public SharePage()
{
InitializeComponent();
}
public NavigatorVM NavigateCommandBooks { get; private set; }
private async void RetourMainMenu(object sender, EventArgs e)
public SharePage(NavigatorVM navigator)
{
await Navigation.PopToRootAsync();
NavigateCommandBooks = navigator;
InitializeComponent();
}
}

@ -10,9 +10,6 @@
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" Padding="5,10,0,10" >
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="RetourMainMenu"/>
</StackLayout.GestureRecognizers>
<Image Source="chevron_left.png">
<Image.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{StaticResource Primary}"/>

@ -1,17 +1,15 @@
using CommunityToolkit.Maui.Views;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class StatutPage : ContentPage
{
public StatutPage()
{
InitializeComponent();
}
public NavigatorVM NavigateCommandBooks { get; private set; }
private async void RetourMainMenu(object sender, EventArgs e)
public StatutPage(NavigatorVM navigator)
{
await Navigation.PopToRootAsync();
NavigateCommandBooks = navigator;
InitializeComponent();
}
}

@ -8,72 +8,41 @@
Shell.ForegroundColor="{StaticResource Primary}">
<ContentPage.Resources>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="Ajouter" Priority="1"
Order="Primary" IconImageSource="plus.png"/>
Order="Primary" IconImageSource="plus.png" Command="{Binding ScanMenuVM.ShowScanMenu}"/>
<ToolbarItem Text="Order" Priority="2"
Order="Primary" IconImageSource="arrows.png"/>
</ContentPage.ToolbarItems>
<ScrollView>
<AbsoluteLayout>
<local:ScanMenuView x:Name="scanMenu" IsVisible="{Binding ScanMenuVM.ScanMenuIsVisible}"
Margin="0, 8, 8, 0"
AbsoluteLayout.LayoutBounds="1, 0"
AbsoluteLayout.LayoutFlags="PositionProportional" ZIndex="1"/>
<StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<ScrollView VerticalOptions="StartAndExpand">
<VerticalStackLayout>
<local:TitledAuthor NameText="Alain Damasio"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="La horde du contrevent" AutorName="Alain Damasio" BookStatus="Non lu">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="La horde du contrevent" AutorName="Alain Damasio" BookStatus="Non lu"/>
<Line Style="{StaticResource LineStyle3}"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="La zone du dehors" AutorName="Alain Damasio" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="La zone du dehors" AutorName="Alain Damasio" BookStatus="Terminé"/>
<local:TitledAuthor NameText="Cixin Lui"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="L'équateur d'Einstein" AutorName="Cixin Liu" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="L'équateur d'Einstein" AutorName="Cixin Liu" BookStatus="Terminé"/>
<Line Style="{StaticResource LineStyle3}"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="La forêt sombre" AutorName="Cixin Liu" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="La forêt sombre" AutorName="Cixin Liu" BookStatus="Terminé"/>
<Line Style="{StaticResource LineStyle3}"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="Le problème à trois corps" AutorName="Cixin Liu" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="Le problème à trois corps" AutorName="Cixin Liu" BookStatus="Terminé"/>
<Line Style="{StaticResource LineStyle3}"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="L'équateur d'Einstein" AutorName="Cixin Liu" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="L'équateur d'Einstein" AutorName="Cixin Liu" BookStatus="Terminé"/>
<Line Style="{StaticResource LineStyle3}"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="La forêt sombre" AutorName="Cixin Liu" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="La forêt sombre" AutorName="Cixin Liu" BookStatus="Terminé"/>
<Line Style="{StaticResource LineStyle3}"/>
<local:ContentViewBook BookImage="buveurencre.jpg" BookName="Le problème à trois corps" AutorName="Cixin Liu" BookStatus="Terminé">
<local:ContentViewBook.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBookSelected"/>
</local:ContentViewBook.GestureRecognizers>
</local:ContentViewBook>
<local:ScanMenuView IsVisible="true"
Margin="0, 8, 8, 0"
AbsoluteLayout.LayoutBounds="1, 0"
AbsoluteLayout.LayoutFlags="PositionProportional"/>
<local:ContentViewBook Command="{Binding NavigateCommandBooks.Navigateto}" CommandParameter="BookDetail" BookImage="buveurencre.jpg" BookName="Le problème à trois corps" AutorName="Cixin Liu" BookStatus="Terminé"/>
</VerticalStackLayout>
</ScrollView>
</StackLayout>
</AbsoluteLayout>
</ContentPage>

@ -1,18 +1,19 @@
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.Views;
using PocketBook.Applicative_VM;
namespace PocketBook;
public partial class TousPage : ContentPage
{
public NavigatorVM NavigateCommandBooks { get; private set; }
public ScanMenuVM ScanMenuVM { get; private set; }
public TousPage()
public TousPage(NavigatorVM navigation)
{
ScanMenuVM = new ScanMenuVM();
NavigateCommandBooks = navigation;
InitializeComponent();
}
public async void OnBookSelected(object sender, EventArgs e)
{
await Navigation.PushAsync(new BookDetail());
BindingContext = this;
}
}

@ -0,0 +1,49 @@
using System;
using Utils;
namespace Stub
{
static class EnumsMapper
{
public static EnumsMapper<Model.Languages, LibraryDTO.Languages> BiddingsMapper { get; }
= new EnumsMapper<Model.Languages, LibraryDTO.Languages>(
Tuple.Create(Model.Languages.Unknown, LibraryDTO.Languages.Unknown),
Tuple.Create(Model.Languages.French, LibraryDTO.Languages.French),
Tuple.Create(Model.Languages.English, LibraryDTO.Languages.English)
);
public static TModel ToModel<TModel, TDTO>(this TDTO dto) where TModel : Enum
where TDTO : Enum
{
foreach(var prop in typeof(EnumsMapper).GetProperties())
{
if(prop.PropertyType.Equals(typeof(EnumsMapper<TModel, TDTO>)))
{
return (prop.GetValue(null) as EnumsMapper<TModel, TDTO>).GetModel(dto);
}
}
return default(TModel);
}
public static Model.Languages ToModel(this LibraryDTO.Languages dto)
=> ToModel<Model.Languages, LibraryDTO.Languages>(dto);
public static TDTO ToDTO<TModel, TDTO>(this TModel model) where TModel : Enum
where TDTO : Enum
{
foreach(var prop in typeof(EnumsMapper).GetProperties())
{
if(prop.PropertyType.Equals(typeof(EnumsMapper<TModel, TDTO>)))
{
return (prop.GetValue(null) as EnumsMapper<TModel, TDTO>).GetEntity(model);
}
}
return default(TDTO);
}
public static LibraryDTO.Languages ToDTO(this Model.Languages model)
=> ToDTO<Model.Languages, LibraryDTO.Languages>(model);
}
}

@ -0,0 +1,93 @@
using System;
using LibraryDTO;
using Model;
using static Stub.Mapper;
namespace Stub
{
public static class Extensions
{
public static Ratings ToPoco(this RatingsDTO dto)
=> new Ratings() { Average = dto.Average, Count = dto.Count };
public static IEnumerable<Ratings> ToPocos(this IEnumerable<RatingsDTO> dtos)
=> dtos.Select(dto => dto.ToPoco());
public static Contributor ToPoco(this ContributorDTO dto)
=> new Contributor { Name = dto.Name, Role = dto.Role };
public static IEnumerable<Contributor> ToPocos(this IEnumerable<ContributorDTO> dtos)
=> dtos.Select(dto => dto.ToPoco());
public static Link ToPoco(this LinkDTO dto)
=> new Link { Title = dto.Title, Url = dto.Url };
public static IEnumerable<Link> ToPocos(this IEnumerable<LinkDTO> dtos)
=> dtos.Select(dto => dto.ToPoco());
public static Author ToPoco(this AuthorDTO dto)
{
var result = AuthorsMapper.GetT(dto);
if(result == null)
{
result = new Author
{
AlternateNames = dto.AlternateNames,
Bio = dto.Bio,
BirthDate = dto.BirthDate,
DeathDate = dto.DeathDate,
Id = dto.Id,
Links = dto.Links.ToPocos().ToList(),
Name = dto.Name
};
}
return result;
}
public static IEnumerable<Author> ToPocos(this IEnumerable<AuthorDTO> dtos)
=> dtos.Select(dto => dto.ToPoco());
public static Work ToPoco(this WorkDTO dto)
{
var result = WorksMapper.GetT(dto);
if(result == null)
{
result = new Work
{
Authors = dto.Authors.ToPocos().ToList(),
Description = dto.Description,
Id = dto.Id,
Ratings = dto.Ratings.ToPoco(),
Subjects = dto.Subjects,
Title = dto.Title
};
}
return result;
}
public static IEnumerable<Work> ToPocos(this IEnumerable<WorkDTO> dtos)
=> dtos.Select(dto => dto.ToPoco());
public static Book ToPoco(this BookDTO dto)
{
var result = BooksMapper.GetT(dto);
if(result == null)
{
result = new Book
{
Authors = dto.Authors.ToPocos().ToList(),
Contributors = dto.Contributors.ToPocos().ToList(),
Format = dto.Format,
Id = dto.Id,
ISBN13 = dto.ISBN13,
Language = dto.Language.ToModel(),
NbPages = dto.NbPages,
PublishDate = dto.PublishDate,
Publishers = dto.Publishers,
Series = dto.Series,
Title = dto.Title,
Works = dto.Works.ToPocos().ToList()
};
}
return result;
}
public static IEnumerable<Book> ToPocos(this IEnumerable<BookDTO> dtos)
=> dtos.Select(dto => dto.ToPoco());
}
}

@ -0,0 +1,49 @@
using Model;
using Stub;
namespace StubLib;
public class LibraryStub : ILibraryManager
{
private StubbedDTO.Stub StubDTO { get; set; } = new StubbedDTO.Stub();
public async Task<Author> GetAuthorById(string id)
{
return (await StubDTO.GetAuthorById(id)).ToPoco();
}
public async Task<Tuple<long, IEnumerable<Author>>> GetAuthorsByName(string substring, int index, int count, string sort = "")
{
var result = await StubDTO.GetAuthorsByName(substring, index, count, sort);
return Tuple.Create(result.Item1, result.Item2.ToPocos());
}
public async Task<Book> GetBookById(string id)
{
return (await StubDTO.GetBookById(id)).ToPoco();
}
public async Task<Book> GetBookByISBN(string isbn)
{
return (await StubDTO.GetBookByISBN(isbn)).ToPoco();
}
public async Task<Tuple<long, IEnumerable<Book>>> GetBooksByAuthor(string author, int index, int count, string sort = "")
{
var result = await StubDTO.GetBooksByAuthor(author, index, count, sort);
return Tuple.Create(result.Item1, result.Item2.ToPocos());
}
public async Task<Tuple<long, IEnumerable<Book>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "")
{
var result = await StubDTO.GetBooksByAuthor(authorId, index, count, sort);
return Tuple.Create(result.Item1, result.Item2.ToPocos());
}
public async Task<Tuple<long, IEnumerable<Book>>> GetBooksByTitle(string title, int index, int count, string sort = "")
{
var result = await StubDTO.GetBooksByTitle(title, index, count, sort);
return Tuple.Create(result.Item1, result.Item2.ToPocos());
}
}

@ -0,0 +1,25 @@
using System;
using static System.Collections.Specialized.BitVector32;
using System.Numerics;
using Utils;
using Model;
using LibraryDTO;
namespace Stub
{
static class Mapper
{
internal static Mapper<Author, AuthorDTO> AuthorsMapper { get; } = new Mapper<Author, AuthorDTO>();
internal static Mapper<Work, WorkDTO> WorksMapper { get; } = new Mapper<Work, WorkDTO>();
internal static Mapper<Book, BookDTO> BooksMapper { get; } = new Mapper<Book, BookDTO>();
internal static void Reset()
{
AuthorsMapper.Reset();
WorksMapper.Reset();
BooksMapper.Reset();
}
}
}

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

@ -0,0 +1,421 @@
using System;
using System.Collections.ObjectModel;
using Model;
using System.Linq;
using LibraryDTO;
using System.Xml.Linq;
namespace StubLib
{
public class UserLibraryStub : IUserLibraryManager
{
public ReadOnlyCollection<Book> Favorites { get; private set; }
private List<Book> favorites = new List<Book>();
public ReadOnlyCollection<Book> Books { get; private set; }
private List<Book> books = new List<Book>();
public ReadOnlyCollection<Contact> Contacts { get; private set; }
private List<Contact> contacts = new List<Contact>();
public ReadOnlyCollection<Loan> Loans { get; private set; }
private List<Loan> loans = new List<Loan>();
public ReadOnlyCollection<Borrowing> Borrowings { get; private set; }
private List<Borrowing> borrowings = new List<Borrowing>();
public ILibraryManager LibraryMgr { get; private set; }
public UserLibraryStub(ILibraryManager libraryMgr)
{
LibraryMgr = libraryMgr;
Favorites = new ReadOnlyCollection<Book>(favorites);
Books = new ReadOnlyCollection<Book>(books);
Borrowings = new ReadOnlyCollection<Borrowing>(borrowings);
Loans = new ReadOnlyCollection<Loan>(loans);
Contacts = new ReadOnlyCollection<Contact>(contacts);
contacts.AddRange(new Contact[]
{
new Contact { Id = "/contacts/01", FirstName = "Audrey", LastName = "Pouclet" },
new Contact { Id = "/contacts/02", FirstName = "Malika", LastName = "More" },
new Contact { Id = "/contacts/03", FirstName = "Antoine" },
});
books.AddRange(new Book[]
{
LibraryMgr.GetBookById("/books/OL25910297M").Result,
LibraryMgr.GetBookById("/books/OL26210208M").Result,
LibraryMgr.GetBookById("/books/OL27258011M").Result,
LibraryMgr.GetBookById("/books/OL28294024M").Result,
LibraryMgr.GetBookById("/books/OL28639494M").Result,
LibraryMgr.GetBookById("/books/OL35699439M").Result,
LibraryMgr.GetBookById("/books/OL37758347M").Result,
LibraryMgr.GetBookById("/books/OL38218739M").Result,
LibraryMgr.GetBookById("/books/OL38586212M").Result,
LibraryMgr.GetBookById("/books/OL8839071M").Result,
LibraryMgr.GetBookById("/books/OL8198056M").Result,
});
books[0].Status = Status.Finished;
books[0].UserNote = "Super bouquin de SF !";
books[0].UserRating = 4.5f;
loans.Add(new Loan { Id = "/loans/01", Book = books[0], Loaner = contacts[0], LoanedAt = new DateTime(2022, 7, 12), ReturnedAt = new DateTime(2023, 9, 1) });
books[1].Status = Status.ToBeRead;
books[2].Status = Status.Finished;
books[2].UserNote = "Des nouvelles de SF. Super auteur à découvrir !";
books[2].UserRating = 4.8f;
books[3].Status = Status.Finished;
books[3].UserRating = 4.0f;
loans.Add(new Loan { Id = "/loans/02", Book = books[3], Loaner = contacts[2], LoanedAt = new DateTime(2020, 12, 23), ReturnedAt = new DateTime(2021, 8, 13) } );
books[4].Status = Status.Finished;
books[4].UserNote = "Déjà moins connu que le premier, et pourtant...";
books[4].UserRating = 4.2f;
books[5].Status = Status.Finished;
books[5].UserNote = "Coup de coeur. Poétique, anarchique, philosophique... + SF. Du Deleuze et du Foucault chez Damasio";
books[5].UserRating = 4.9f;
books[6].Status = Status.NotRead;
books[7].Status = Status.Finished;
books[7].UserRating = 4.9f;
books[7].UserNote = "Chef d'oeuvre";
books[8].Status = Status.Finished;
books[8].UserRating = 4.2f;
books[8].UserNote = "Des nouvelles très réussies dont Rapport Minoritaire";
books[9].Status = Status.ToBeRead;
books[9].Status = Status.Reading;
borrowings.Add(new Borrowing
{
Id = "/borrowing/01", Owner = contacts[0],
Book = LibraryMgr.GetBookById("/books/OL27328194M").Result,
BorrowedAt = new DateTime(2023, 9, 7)
});
borrowings.Add(new Borrowing
{
Id = "/borrowing/02", Owner = contacts[1],
Book = LibraryMgr.GetBookById("/books/OL27989051M").Result,
BorrowedAt = new DateTime(2022, 7, 7),
ReturnedAt = new DateTime(2023, 3, 1)
});
borrowings.Add(new Borrowing
{
Id = "/borrowing/03", Owner = contacts[1],
Book = LibraryMgr.GetBookById("/books/OL35698073M").Result,
BorrowedAt = new DateTime(2022, 7, 7),
ReturnedAt = new DateTime(2022, 9, 1)
});
borrowings.Add(new Borrowing
{
Id = "/borrowing/04", Owner = contacts[1],
Book = LibraryMgr.GetBookById("/books/OL35698083M").Result,
BorrowedAt = new DateTime(2022, 7, 7),
ReturnedAt = new DateTime(2023, 8, 30)
});
}
public Task<Book> AddBook(Book book)
{
if(Books.Contains(book))
{
return Task.FromResult<Book>(null);
}
books.Add(book);
return Task.FromResult(book);
}
public async Task<Book> AddBook(string id)
{
if(Books.SingleOrDefault(b => b.Id == id) != null)
{
return null;
}
var book = await LibraryMgr.GetBookById(id);
books.Add(book);
return book;
}
public async Task<Book> AddBookByIsbn(string isbn)
{
if(Books.SingleOrDefault(b => b.ISBN13 == isbn) != null)
{
return null;
}
var book = await LibraryMgr.GetBookByISBN(isbn);
books.Add(book);
return book;
}
public Task<bool> RemoveBook(Book book)
{
return Task.FromResult(books.Remove(book));
}
public Task<bool> RemoveBook(string id)
{
return Task.FromResult(books.RemoveAll(b => b.Id == id) >= 0);
}
public Task<bool> RemoveBookByIsbn(string isbn)
{
return Task.FromResult(books.RemoveAll(b => b.ISBN13 == isbn) >= 0);
}
public Task<bool> AddToFavorites(Book book)
{
if(Favorites.Contains(book))
{
return Task.FromResult(false);
}
var bookToAdd = Books.SingleOrDefault(b => b.Id == book.Id);
if(bookToAdd == null)
{
return Task.FromResult(false);
}
favorites.Add(bookToAdd);
return Task.FromResult(true);
}
public Task<bool> AddToFavorites(string bookId)
{
if(Favorites.SingleOrDefault(b => b.Id == bookId) != null)
{
return Task.FromResult(false);
}
var book = Books.SingleOrDefault(b => b.Id == bookId);
if(book == null)
{
return Task.FromResult(false);
}
favorites.Add(book);
return Task.FromResult(true);
}
public Task<bool> RemoveFromFavorites(Book book)
{
return Task.FromResult(favorites.Remove(book));
}
public Task<bool> RemoveFromFavorites(string bookId)
{
return Task.FromResult(favorites.RemoveAll(b => b.Id == bookId) >= 0);
}
public Task<Contact> AddContact(Contact contact)
{
if(Contacts.Contains(contact))
{
return Task.FromResult<Contact>(null);
}
contacts.Add(contact);
return Task.FromResult(contact);
}
public Task<bool> RemoveContact(Contact contact)
{
return Task.FromResult(contacts.Remove(contact));
}
public Task<bool> LendBook(Book book, Contact contact, DateTime? loanDate = null)
{
if(!Books.Contains(book))
return Task.FromResult(false);
if(!Contacts.Contains(contact))
AddContact(contact);
Loan loan = new Loan { Book = book, Loaner = contact, LoanedAt = loanDate.GetValueOrDefault(DateTime.Now) };
if(Loans.Contains(loan))
return Task.FromResult(false);
loans.Add(loan);
return Task.FromResult(true);
}
public Task<bool> GetBackBook(Book book, DateTime? returnedDate = null)
{
if(!Books.Contains(book))
return Task.FromResult(false);
var loan = loans.SingleOrDefault(l => l.Book == book);
if(loan == null)
return Task.FromResult(false);
loan.ReturnedAt = returnedDate.GetValueOrDefault(DateTime.Now);
return Task.FromResult(true);
}
public Task<bool> BorrowBook(Book book, Contact owner, DateTime? borrowedDate = null)
{
if(!Books.Contains(book))
return Task.FromResult(false);
if(!Contacts.Contains(owner))
AddContact(owner);
Borrowing borrow = new Borrowing { Book = book, Owner = owner, BorrowedAt = borrowedDate.GetValueOrDefault(DateTime.Now) };
if(Borrowings.Contains(borrow))
return Task.FromResult(false);
borrowings.Add(borrow);
return Task.FromResult(true);
}
public Task<bool> GiveBackBook(Book book, DateTime? returnedDate = null)
{
if(!Books.Contains(book))
return Task.FromResult(false);
var borrow = borrowings.SingleOrDefault(b => b.Book == book);
if(borrow == null)
return Task.FromResult(false);
borrow.ReturnedAt = returnedDate.GetValueOrDefault(DateTime.Now);
return Task.FromResult(true);
}
public Task<Book> GetBookById(string id)
{
return Task.FromResult(Books.SingleOrDefault(b => b.Id == id));
}
public Task<Book> GetBookByISBN(string isbn)
{
return Task.FromResult(Books.SingleOrDefault(b => b.ISBN13 == isbn));
}
public Task<Tuple<long, IEnumerable<Book>>> GetBooksByTitle(string title, int index, int count, string sort = "")
{
var foundBooks = Books.Where(b => b.Title.Contains(title, StringComparison.InvariantCultureIgnoreCase));
return OrderBooks(foundBooks, index, count, sort);
}
public Task<Tuple<long, IEnumerable<Book>>> GetBooksByAuthorId(string authorId, int index, int count, string sort = "")
{
var foundBooks = Books.Where(b => b.Authors.Exists(a => a.Id.Contains(authorId))
|| b.Works.Exists(w => w.Authors.Exists(a => a.Id.Contains(authorId))));
return OrderBooks(books, index, count, sort);
}
public Task<Tuple<long, IEnumerable<Book>>> GetBooksByAuthor(string author, int index, int count, string sort = "")
{
var foundBooks = Books.Where(b => ContainsAuthorName(b, author));
return OrderBooks(books, index, count, sort);
}
public IEnumerable<Author> Authors
{
get
{
var bookAuthors = Books.SelectMany(b => b.Authors);
var workAuthors = Books.SelectMany(b => b.Works).SelectMany(w => w.Authors);
return bookAuthors.Union(workAuthors).Distinct();
}
}
public Task<Author> GetAuthorById(string id)
{
return Task.FromResult(Authors.SingleOrDefault(a => a.Id == id));
}
private Task<Tuple<long, IEnumerable<Author>>> OrderAuthors(IEnumerable<Author> authors, int index, int count, string sort = "")
{
switch(sort)
{
case "name":
authors = authors.OrderBy(a => a.Name);
break;
case "name_reverse":
authors = authors.OrderByDescending(a => a.Name);
break;
}
return Task.FromResult(Tuple.Create((long)authors.Count(), authors.Skip(index*count).Take(count)));
}
public Task<Tuple<long, IEnumerable<Author>>> GetAuthorsByName(string substring, int index, int count, string sort = "")
{
var foundAuthors = Authors.Where(a => a.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase)
|| a.AlternateNames.Exists(alt => alt.Contains(substring, StringComparison.InvariantCultureIgnoreCase)));
return OrderAuthors(foundAuthors, index, count, sort);
}
public Task<Book> UpdateBook(Book updatedBook)
{
if(!books.Contains(updatedBook))
{
return Task.FromResult<Book>(null);
}
books.Remove(updatedBook);
books.Add(updatedBook);
return Task.FromResult(updatedBook);
}
private Task<Tuple<long, IEnumerable<Book>>> OrderBooks(IEnumerable<Book> books, int index, int count, string sort = "")
{
switch(sort)
{
case "title":
books = books.OrderBy(b => b.Title);
break;
case "title_reverse":
books = books.OrderByDescending(b => b.Title);
break;
case "new":
books = books.OrderByDescending(b => b.PublishDate);
break;
case "old":
books = books.OrderBy(b => b.PublishDate);
break;
}
return Task.FromResult(Tuple.Create(books.LongCount(), books.Skip(index*count).Take(count)));
}
private bool ContainsAuthorName(Book book, string name)
{
IEnumerable<Author> authors = new List<Author>();
if(book.Authors != null && book.Authors.Count > 0)
{
authors = authors.Union(book.Authors);
}
if(book.Works != null)
{
var worksAuthors = book.Works.SelectMany(w => w.Authors).ToList();
if(worksAuthors.Count > 0)
authors = authors.Union(worksAuthors);
}
foreach(var author in authors)
{
if(author.Name.Contains(name, StringComparison.OrdinalIgnoreCase)
|| author.AlternateNames.Exists(alt => alt.Contains(name, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
}
return false;
}
public Task<Tuple<long, IEnumerable<Book>>> GetBooksFromCollection(int index, int count, string sort = "")
{
return OrderBooks(Books, index, count, sort);
}
public Task<Tuple<long, IEnumerable<Loan>>> GetCurrentLoans(int index, int count)
{
var currentLoans = Loans.Where(l => !l.ReturnedAt.HasValue);
return Task.FromResult(Tuple.Create(currentLoans.LongCount(), currentLoans.Skip(index*count).Take(count)));
}
public Task<Tuple<long, IEnumerable<Loan>>> GetPastLoans(int index, int count)
{
var currentLoans = Loans.Where(l => l.ReturnedAt.HasValue);
return Task.FromResult(Tuple.Create(currentLoans.LongCount(), currentLoans.Skip(index*count).Take(count)));
}
public Task<Tuple<long, IEnumerable<Borrowing>>> GetCurrentBorrowings(int index, int count)
{
var currentBorrowings = Borrowings.Where(l => !l.ReturnedAt.HasValue);
return Task.FromResult(Tuple.Create(currentBorrowings.LongCount(), currentBorrowings.Skip(index*count).Take(count)));
}
public Task<Tuple<long, IEnumerable<Borrowing>>> GetPastBorrowings(int index, int count)
{
var currentBorrowings = Borrowings.Where(l => l.ReturnedAt.HasValue);
return Task.FromResult(Tuple.Create(currentBorrowings.LongCount(), currentBorrowings.Skip(index*count).Take(count)));
}
public Task<Tuple<long, IEnumerable<Contact>>> GetContacts(int index, int count)
{
return Task.FromResult(Tuple.Create(Contacts.LongCount(), Contacts.Skip(index*count).Take(count)));
}
}
}

@ -0,0 +1,208 @@
using System.Data.SqlTypes;
using System.Reflection;
using DtoAbstractLayer;
using JsonReader;
using LibraryDTO;
using static System.Reflection.Metadata.BlobBuilder;
namespace StubbedDTO;
public class Stub : IDtoManager
{
public static List<AuthorDTO> Authors { get; set; } = new List<AuthorDTO>();
public static List<BookDTO> Books { get; set; } = new List<BookDTO>();
public static List<WorkDTO> Works { get; set; } = new List<WorkDTO>();
public static Assembly Assembly => typeof(Stub).Assembly;
static Stub()
{
foreach(var resource in Assembly.GetManifestResourceNames().Where(n => n.Contains("authors")))
{
using(Stream stream = Assembly.GetManifestResourceStream(resource))
using(StreamReader reader = new StreamReader(stream))
{
Authors.Add(AuthorJsonReader.ReadAuthor(reader.ReadToEnd()));
}
}
foreach(var resource in Assembly.GetManifestResourceNames().Where(n => n.Contains("works")))
{
var ratingsResource = resource.Insert(resource.LastIndexOf('.'), ".ratings").Replace("works", "ratings");
using(Stream stream = Assembly.GetManifestResourceStream(resource))
using(StreamReader reader = new StreamReader(stream))
using(Stream streamRatings = Assembly.GetManifestResourceStream(ratingsResource))
using(StreamReader readerRatings = new StreamReader(streamRatings))
{
var work = WorkJsonReader.ReadWork(reader.ReadToEnd(), readerRatings.ReadToEnd());
if(work.Authors != null)
foreach(var author in work.Authors.ToList())
{
var newAuthor = Authors.SingleOrDefault(a => a.Id == author.Id);
work.Authors.Remove(author);
work.Authors.Add(newAuthor);
}
Works.Add(work);
}
}
foreach(var resource in Assembly.GetManifestResourceNames().Where(n => n.Contains("books")))
{
using(Stream stream = Assembly.GetManifestResourceStream(resource))
using(StreamReader reader = new StreamReader(stream))
{
var book = BookJsonReader.ReadBook(reader.ReadToEnd());
foreach(var author in book.Authors.ToList())
{
var newAuthor = Authors.SingleOrDefault(a => a.Id == author.Id);
book.Authors.Remove(author);
book.Authors.Add(newAuthor);
}
foreach(var work in book.Works.ToList())
{
var newWork = Works.SingleOrDefault(w => w.Id == work.Id);
book.Works.Remove(work);
book.Works.Add(newWork);
}
Books.Add(book);
}
}
}
public Task<AuthorDTO> GetAuthorById(string id)
{
var author = Stub.Authors.SingleOrDefault(a => a.Id.Contains(id));
return Task.FromResult(author);
}
private Task<Tuple<long, IEnumerable<AuthorDTO>>> OrderAuthors(IEnumerable<AuthorDTO> authors, int index, int count, string sort = "")
{
switch(sort)
{
case "name":
authors = authors.OrderBy(a => a.Name);
break;
case "name_reverse":
authors = authors.OrderByDescending(a => a.Name);
break;
}
return Task.FromResult(Tuple.Create((long)authors.Count(), authors.Skip(index*count).Take(count)));
}
public async Task<Tuple<long, IEnumerable<AuthorDTO>>> GetAuthors(int index, int count, string sort = "")
{
IEnumerable<AuthorDTO> authors = Stub.Authors;
return await OrderAuthors(authors, index, count, sort);
}
public async Task<Tuple<long, IEnumerable<AuthorDTO>>> GetAuthorsByName(string name, int index, int count, string sort = "")
{
var authors = Stub.Authors.Where(a => a.Name.Contains(name, StringComparison.OrdinalIgnoreCase)
|| a.AlternateNames.Exists(alt => alt.Contains(name, StringComparison.OrdinalIgnoreCase)));
return await OrderAuthors(authors, index, count, sort);
}
public Task<BookDTO> GetBookById(string id)
{
var book = Stub.Books.SingleOrDefault(b => b.Id.Contains(id));
return Task.FromResult(book);
}
private Task<Tuple<long, IEnumerable<BookDTO>>> OrderBooks(IEnumerable<BookDTO> books, int index, int count, string sort = "")
{
switch(sort)
{
case "title":
books = books.OrderBy(b => b.Title);
break;
case "title_reverse":
books = books.OrderByDescending(b => b.Title);
break;
case "new":
books = books.OrderByDescending(b => b.PublishDate);
break;
case "old":
books = books.OrderBy(b => b.PublishDate);
break;
}
return Task.FromResult(Tuple.Create(books.LongCount(), books.Skip(index*count).Take(count)));
}
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooks(int index, int count, string sort = "")
{
var books = Stub.Books;
return await OrderBooks(books, index, count, sort);
}
public Task<BookDTO> GetBookByISBN(string isbn)
{
var book = Stub.Books.SingleOrDefault(b => b.ISBN13.Equals(isbn, StringComparison.OrdinalIgnoreCase));
return Task.FromResult(book);
}
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByTitle(string title, int index, int count, string sort = "")
{
var books = Stub.Books.Where(b => b.Title.Contains(title, StringComparison.OrdinalIgnoreCase)
|| b.Series.Exists(s => s.Contains(title, StringComparison.OrdinalIgnoreCase)));
return await OrderBooks(books, index, count, 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.Contains(authorId))
|| b.Works.Exists(w => w.Authors.Exists(a => a.Id.Contains(authorId))));
return await OrderBooks(books, index, count, sort);
}
public async Task<Tuple<long, IEnumerable<BookDTO>>> GetBooksByAuthor(string name, int index, int count, string sort = "")
{
var books = Stub.Books.Where(b => ContainsAuthorName(b, name));
return await OrderBooks(books, index, count, sort);
}
private bool ContainsAuthorName(BookDTO book, string name)
{
IEnumerable<AuthorDTO> authors = new List<AuthorDTO>();
if(book.Authors != null && book.Authors.Count > 0)
{
authors = authors.Union(book.Authors);
}
if(book.Works != null)
{
var worksAuthors = book.Works.SelectMany(w => w.Authors).ToList();
if(worksAuthors.Count > 0)
authors = authors.Union(worksAuthors);
}
foreach(var author in authors)
{
if(author.Name.Contains(name, StringComparison.OrdinalIgnoreCase)
|| author.AlternateNames.Exists(alt => alt.Contains(name, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
}
return false;
}
public Task<Tuple<long, IEnumerable<WorkDTO>>> GetWorks(int index, int count)
{
long nbWorks = Stub.Works.Count;
var works = Stub.Works.Skip(index*count).Take(count);
return Task.FromResult(Tuple.Create(nbWorks, works));
}
public Task<long> GetNbAuthors()
=> Task.FromResult((long)Stub.Authors.Count);
public Task<long> GetNbBooks()
=> Task.FromResult((long)Stub.Books.Count);
public Task<long> GetNbWorks()
=> Task.FromResult((long)Stub.Works.Count);
}

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<!--<ItemGroup>
<None Remove="books\" />
<None Remove="authors\" />
<None Remove="works\" />
<None Remove="ratings\" />
<None Remove="authors\OL13066A.json" />
</ItemGroup>-->
<ItemGroup>
<Folder Include="books\" />
<Folder Include="authors\" />
<Folder Include="works\" />
<Folder Include="ratings\" />
</ItemGroup>
<!--<ItemGroup>
<None Include="*\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>-->
<ItemGroup>
<ProjectReference Include="..\DtoAbstractLayer\DtoAbstractLayer.csproj" />
<ProjectReference Include="..\JsonReader\JsonReader.csproj" />
<ProjectReference Include="..\LibraryDTO\LibraryDTO.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="*\*.json" />
</ItemGroup>
</Project>

@ -0,0 +1 @@
{"alternate_names": ["Herbert George Wells", "H. Wells"], "type": {"key": "/type/author"}, "key": "/authors/OL13066A", "photos": [14364506, 6255164, 6255163, 5542282], "birth_date": "21 September 1866", "wikipedia": "http://en.wikipedia.org/wiki/H._G._Wells", "title": "(Wells, H. G.(Herbert George))", "fuller_name": "Herbert George Wells", "remote_ids": {"project_gutenberg": "30", "librarything": "wellshg", "goodreads": "880695", "wikidata": "Q42511", "librivox": "146", "isni": "0000000122832703", "viaf": "97006424", "amazon": "B08JKH6BRG"}, "personal_name": "H. G. Wells", "source_records": ["amazon:0353051969", "amazon:1545593752", "amazon:0353360031", "amazon:0343204800", "amazon:0900948442", "amazon:0359695256", "amazon:1541399528", "bwb:9798748338851", "bwb:9798711942399", "amazon:154230038X", "bwb:9798515967789", "amazon:1548545791", "amazon:167576655X", "amazon:1096406985", "amazon:0342716778", "amazon:0343312492", "amazon:1642262722", "amazon:1539515087", "bwb:9798515810764", "bwb:9798594591745", "amazon:2329219377", "bwb:9798588040884", "amazon:151971002X", "amazon:2070500179", "amazon:1535132841", "ia:warofworlds0000well_p4j3", "bwb:9798589404036", "amazon:8380321359", "bwb:9798549302624", "amazon:1530609798", "amazon:1420938495", "amazon:1006147551", "bwb:9798458602198", "bwb:9798520694045", "bwb:9780342740260", "bwb:9798541851458", "bwb:9798653436154", "amazon:9385018116", "amazon:1014334756", "bwb:9798459326819", "bwb:9798796783801", "amazon:1006147721", "amazon:1536901911", "bwb:9798442116618", "bwb:9798446719273", "bwb:9798715872197", "bwb:9798411853216", "bwb:9781398812130", "bwb:9798537902386", "bwb:9798537873945", "bwb:9798578578908", "bwb:9798760273000", "bwb:9798708376572", "bwb:9780342996100", "promise:bwb_daily_pallets_2022-03-17", "bwb:9798833824375", "ia:warofworldsillus0000well", "bwb:9781958437681", "amazon:1298631807", "bwb:9798709836068", "promise:bwb_daily_pallets_2022-08-23", "bwb:9798685274663", "promise:bwb_daily_pallets_2022-08-01", "bwb:9781728974033", "bwb:9781731472441", "bwb:9781790605743", "marc:marc_columbia/Columbia-extract-20221130-030.mrc:206198520:1318", "bwb:9798840336311", "promise:bwb_daily_pallets_2022-12-19", "amazon:8461947932", "bwb:9798364688729", "amazon:201998198X", "bwb:9781549506130"], "name": "H. G. Wells", "death_date": "13 August 1946", "bio": "Herbert George Wells was an English author, best known for his work in the science fiction genre. He was also a prolific writer in many genres, including contemporary novels, history, politics and social commentary.", "latest_revision": 44, "revision": 44, "created": {"type": "/type/datetime", "value": "2008-04-01T03:28:50.625462"}, "last_modified": {"type": "/type/datetime", "value": "2023-08-31T06:46:36.893522"}}

@ -0,0 +1 @@
{"location": "Paris, France", "photos": [5548027], "type": {"key": "/type/author"}, "death_date": "24 November 1985", "alternate_names": ["Rene\u0301 Barjavel", "Barjavel Ren\u00e9", "Rene Barjavel", "Barjavel Rene"], "key": "/authors/OL152472A", "source_records": ["amazon:8498724929", "promise:bwb_daily_pallets_2022-03-17", "promise:bwb_daily_pallets_2021-02-03", "amazon:2070361691", "promise:bwb_daily_pallets_2021-02-17"], "birth_date": "1911-01-24", "name": "Ren\u00e9 Barjavel", "bio": "Ren\u00e9 Barjavel born in Nyons, France, the son of a baker. In 1914, while his father served in World War I, his mother ran the bakery, and he was left alone much of the time to discover the world through exploration and reading. His his mother died of sleeping sickness in 1922, when he was just 11 years old, and he was sent to boarding school in Nyons. In 1923, when his father was unable to pay the school's fees to continue his studies, he became the proteg\u00e9 of the school's director, Abel Boisselier, and accompanied him to the college in Cusset. During his stay there, which lasted until he ran out of funds in 1927, he continued to study literature. After leaving school, he worked worked several jobs, including as a real estate agent and a bank employee, until 1929 when he became a journalist in Progress Allier in Moulins.\r\n\r\nIn 1935, he met the publisher Robert Deno\u00ebl, and he moved to Paris to work at \u00c9ditions Deno\u00ebl. In 1936 he married Madeleine de Wattripont. While working at Deno\u00ebl, he continued to work as a journalist for the weekly Le Merle Blanc, where he wrote film reviews. In 1939, he joined the war with Germany and was sent to the Pyrenees, but returned to Paris when the armistice was declared and Deno\u00ebl re-opened his publishing house. During this time, his first novel, Roland, le chevalier plus fort que le lion (Roland, the Knight More Proud than the Lion) (1942), was published, with help from Deno\u00ebl. He wrote Le Voyageur imprudent (Future Times Three) in 1943, and became the first writer to present the famous grandfather paradox of time travel. In 1944 he became literary director at \u00c9ditions Deno\u00ebl. In 1945, Deno\u00ebl was killed.\r\n\r\nAfter the war, and the failure of his latest novel, Le diable l'emporte (The Devil Wins) (1948), he left novel-writing for the cinema. However, he contracted tuberculosis and ran out of money before completing his first project, \"Barabbas, pour qui Dieu ne fut qu\u2019un temps\". He spent some time recovering in the south of France, returning to Paris in 1951.\r\n\r\nHe worked as a screenwriter in Paris, and in 1962 he became involved in science fiction, at that time a growing fiction genre in France. He published the novel Colomb de la lune (Columbus of the Moon) in 1962. In 1968 he published La Nuit des temps (The Ice People), which was very successful and popular, and won the Prix des libraires. In 1969, he began a weekly column in the Sunday newspaper Les Libres Propos. In 1972, he was a co-founder of the Prix de science-fiction Apollo, and was on the jury. In 1981, at age 70, he stopped writing his columns in the Journal du Dimanche and resumed writing novels. He died in 1985, having written over 25 novels and several screenplays.", "wikipedia": "http://en.wikipedia.org/wiki/Ren%C3%A9_Barjavel", "personal_name": "Ren\u00e9 Barjavel", "remote_ids": {"viaf": "99967531", "wikidata": "Q562556", "isni": "0000000109285020"}, "latest_revision": 7, "revision": 7, "created": {"type": "/type/datetime", "value": "2008-04-01T03:28:50.625462"}, "last_modified": {"type": "/type/datetime", "value": "2023-02-20T12:24:20.987187"}}

@ -0,0 +1 @@
{"photos": [8567902], "created": {"type": "/type/datetime", "value": "2008-04-01T03:28:50.625462"}, "latest_revision": 5, "name": "Ted Chiang", "key": "/authors/OL1604887A", "personal_name": "Ted Chiang", "revision": 5, "type": {"key": "/type/author"}, "last_modified": {"type": "/type/datetime", "value": "2020-09-30T12:42:07.389520"}, "remote_ids": {"viaf": "85807457", "wikidata": "Q503095", "isni": "0000000109218979"}}

@ -0,0 +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
}

@ -0,0 +1 @@
{"name": "Michael A. Hiltzik", "personal_name": "Michael A. Hiltzik", "last_modified": {"type": "/type/datetime", "value": "2008-09-07 09:06:34.939911"}, "key": "/authors/OL239209A", "type": {"key": "/type/author"}, "id": 623786, "revision": 2}

@ -0,0 +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"
}
}

@ -0,0 +1 @@
{"name": "Pennac", "last_modified": {"type": "/type/datetime", "value": "2008-04-30 08:14:56.482104"}, "key": "/authors/OL3113661A", "type": {"key": "/type/author"}, "id": 11969949, "revision": 1}

@ -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}

@ -0,0 +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
}

@ -0,0 +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"
}
}

@ -0,0 +1 @@
{"created": {"type": "/type/datetime", "value": "2008-04-01T03:28:50.625462"}, "latest_revision": 6, "name": "Daniel Pennac", "key": "/authors/OL55888A", "personal_name": "Daniel Pennac", "birth_date": "1944", "revision": 6, "type": {"key": "/type/author"}, "last_modified": {"type": "/type/datetime", "value": "2020-09-30T12:22:32.446485"}, "remote_ids": {"viaf": "29539018", "wikidata": "Q332689", "isni": "0000000121258295"}}

@ -0,0 +1 @@
{"birth_date": "1802", "key": "/authors/OL5989984A", "personal_name": "Alexandre Dumas", "remote_ids": {"wikidata": "Q38337", "isni": "0000000121012885", "viaf": "51688902"}, "source_records": ["ia:lacomtessedechar0000duma_c0h3", "amazon:0343253933", "ia:sanjianke0008dazh", "amazon:1087918545", "amazon:1361580682", "amazon:1374919551", "amazon:2091870757", "amazon:2363583477", "bwb:9798725078565", "amazon:1374990906", "amazon:1374997609", "amazon:1374828378", "ia:lestroismousquet0000duma_h9z1", "amazon:1080726861", "amazon:1376310082", "amazon:1530054605", "amazon:1548458821", "amazon:2743400463", "amazon:1535180129"], "alternate_names": ["Da Zhong Ma", "Alexander Dumas", "Alejandro Dumas"], "type": {"key": "/type/author"}, "death_date": "1870", "name": "Alexandre Dumas", "title": "(Dumas, Alexandre, 1802-1870)", "photos": [12919454], "latest_revision": 30, "revision": 30, "created": {"type": "/type/datetime", "value": "2008-10-27T00:19:50.151000"}, "last_modified": {"type": "/type/datetime", "value": "2022-09-28T00:27:01.465804"}}

@ -0,0 +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"
}
}

@ -0,0 +1 @@
{"source_records": ["bwb:9781801108454"], "type": {"key": "/type/author"}, "personal_name": "Adrian Tchaikovsky", "links": [{"title": "Wikipedia", "url": "https://en.wikipedia.org/wiki/Adrian_Tchaikovsky", "type": {"key": "/type/link"}}], "birth_date": "1972", "name": "Adrian Tchaikovsky", "key": "/authors/OL7468980A", "remote_ids": {"storygraph": "5a684299-8db8-4c82-8f0c-26f2c6c8ad7b", "viaf": "102929550", "wikidata": "Q4685389"}, "latest_revision": 8, "revision": 8, "created": {"type": "/type/datetime", "value": "2019-03-06T04:53:32.329596"}, "last_modified": {"type": "/type/datetime", "value": "2023-08-07T23:24:42.671051"}}

@ -0,0 +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"
}
}

@ -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"
}
}

@ -0,0 +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"
}
}

@ -0,0 +1,35 @@
{
"publishers": [ "Denoe\u0308l" ],
"title": "Le voyageur imprudent.",
"notes": {
"type": "/type/text",
"value": "French text."
},
"identifiers": {
"librarything": [ "182578" ],
"goodreads": [ "106786" ]
},
"covers": [ 7267304 ],
"languages": [ { "key": "/languages/fre" } ],
"isbn_10": [ "2070364852" ],
"isbn_13": [ "9782070364852" ],
"publish_date": "1958",
"publish_country": "fr ",
"key": "/books/OL18547803M",
"authors": [ { "key": "/authors/OL152472A" } ],
"publish_places": [ "Paris" ],
"works": [ { "key": "/works/OL11466820W" } ],
"type": { "key": "/type/edition" },
"local_id": [ "urn:bwbsku:O6-CHO-999" ],
"source_records": [ "promise:bwb_daily_pallets_2022-05-23" ],
"latest_revision": 7,
"revision": 7,
"created": {
"type": "/type/datetime",
"value": "2008-10-17T19:42:31.933490"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-01-14T06:54:32.286139"
}
}

@ -0,0 +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"
}
}

@ -0,0 +1,30 @@
{
"publishers": [ "Gallimard" ],
"number_of_pages": 736,
"covers": [ 7891413 ],
"physical_format": "Paperback",
"key": "/books/OL26210208M",
"isbn_13": [ "9782070464234" ],
"pagination": "700",
"classifications": {},
"title": "La Horde du Contrevent",
"identifiers": { "goodreads": [ "25135826" ] },
"languages": [ { "key": "/languages/fre" } ],
"isbn_10": [ "2070464237" ],
"publish_date": "2015",
"works": [ { "key": "/works/OL15505243W" } ],
"type": { "key": "/type/edition" },
"physical_dimensions": "17.8 x 10.8 x centimeters",
"local_id": [ "urn:bwbsku:W7-DET-916" ],
"source_records": [ "promise:bwb_daily_pallets_2022-09-01" ],
"latest_revision": 5,
"revision": 5,
"created": {
"type": "/type/datetime",
"value": "2017-01-01T23:14:47.314119"
},
"last_modified": {
"type": "/type/datetime",
"value": "2022-12-04T13:20:06.799989"
}
}

@ -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,26 @@
{
"publishers": [ "Folio", "FOLIO", "GALLIMARD" ],
"source_records": [ "amazon:207285329X" ],
"title": "Dans la toile du temps",
"identifiers": { "amazon": [ "207285329X" ] },
"isbn_13": [ "9782072853296" ],
"covers": [ 8792712 ],
"physical_format": "mass market paperback",
"isbn_10": [ "207285329X" ],
"publish_date": "2019",
"key": "/books/OL27328194M",
"authors": [ { "key": "/authors/OL7468980A" } ],
"works": [ { "key": "/works/OL20148560W" } ],
"type": { "key": "/type/edition" },
"number_of_pages": 704,
"latest_revision": 2,
"revision": 2,
"created": {
"type": "/type/datetime",
"value": "2019-10-02T21:11:02.625762"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-04-07T18:08:17.514602"
}
}

@ -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,32 @@
{
"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"
}
}

@ -0,0 +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"
}
}

@ -0,0 +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"
}
}

@ -0,0 +1,29 @@
{
"type": { "key": "/type/edition" },
"title": "Tour de Babylone",
"authors": [ { "key": "/authors/OL1604887A" } ],
"publish_date": "Apr 01, 2010",
"source_records": [ "amazon:2070406881" ],
"number_of_pages": 416,
"publishers": [ "Gallimard Education" ],
"isbn_10": [ "2070406881" ],
"isbn_13": [ "9782070406883" ],
"physical_format": "pocket book",
"notes": {
"type": "/type/text",
"value": "Source title: Tour de Babylone (Folio Science Fiction) (French Edition)"
},
"works": [ { "key": "/works/OL27687397W" } ],
"key": "/books/OL37758347M",
"covers": [ 14058074 ],
"latest_revision": 2,
"revision": 2,
"created": {
"type": "/type/datetime",
"value": "2022-03-15T04:11:29.461242"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-04-16T15:24:11.712871"
}
}

@ -0,0 +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"
}
}

@ -0,0 +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"
}
}

@ -0,0 +1,38 @@
{
"publishers": [ "Collins" ],
"identifiers": {
"goodreads": [ "1101290" ],
"librarything": [ "68418" ]
},
"subtitle": "Xerox PARC and the Dawn of the Computer Age",
"weight": "13.9 ounces",
"covers": [ 684348 ],
"physical_format": "Paperback",
"key": "/books/OL8198056M",
"authors": [ { "key": "/authors/OL239209A" } ],
"subjects": [ "Industries - General", "Research", "Corporate & Business History - General", "California", "Business & Economics", "Business / Economics / Finance", "Palo Alto Research Center", "Business/Economics", "Palo Alto", "History", "Computer Industry", "Business & Economics / Industries", "Xerox Corporation.", "Computer Science" ],
"languages": [ { "key": "/languages/eng" } ],
"first_sentence": {
"type": "/type/text",
"value": "The photograph shows a handsome man in a checked sport shirt, his boyish face half-obscured by a cloud of pipe smoke."
},
"title": "Dealers of Lightning",
"number_of_pages": 448,
"isbn_13": [ "9780887309892" ],
"isbn_10": [ "0887309895" ],
"publish_date": "April 4, 2000",
"works": [ { "key": "/works/OL1987733W" } ],
"type": { "key": "/type/edition" },
"physical_dimensions": "8.1 x 5.3 x 1.2 inches",
"source_records": [ "bwb:9780887309892" ],
"latest_revision": 7,
"revision": 7,
"created": {
"type": "/type/datetime",
"value": "2008-04-29T15:03:11.581851"
},
"last_modified": {
"type": "/type/datetime",
"value": "2021-12-27T03:34:56.026678"
}
}

@ -0,0 +1,34 @@
{
"publishers": [ "Editions Flammarion" ],
"number_of_pages": 420,
"weight": "7 ounces",
"covers": [ 968374 ],
"physical_format": "Mass Market Paperback",
"last_modified": {
"type": "/type/datetime",
"value": "2020-07-15T08:55:39.381795"
},
"latest_revision": 7,
"key": "/books/OL8839071M",
"authors": [ { "key": "/authors/OL3113661A" } ],
"subjects": [ "Language readers" ],
"edition_name": "1 edition",
"languages": [ { "key": "/languages/fre" } ],
"classifications": {},
"title": "La Petite Marchande De Prose",
"identifiers": {
"librarything": [ "586212" ],
"goodreads": [ "355884" ]
},
"created": {
"type": "/type/datetime",
"value": "2008-04-30T08:14:56.482104"
},
"isbn_13": [ "9782070403684" ],
"isbn_10": [ "2070403688" ],
"publish_date": "October 3, 1997",
"works": [ { "key": "/works/OL705036W" } ],
"type": { "key": "/type/edition" },
"physical_dimensions": "7 x 4.2 x 0.9 inches",
"revision": 7
}

@ -0,0 +1,29 @@
{
"publishers": [ "Hachette" ],
"physical_format": "Unknown Binding",
"source_records": [ "amazon:2010144236:2-au:1680493645:142556", "promise:bwb_daily_pallets_2020-11-19", "promise:bwb_daily_pallets_2021-03-30" ],
"title": "L'homme Invisible",
"identifiers": {
"librarything": [ "21214" ],
"goodreads": [ "1337072" ]
},
"isbn_13": [ "9782010144233" ],
"isbn_10": [ "2010144236" ],
"key": "/books/OL9797565M",
"authors": [ { "key": "/authors/OL13066A" } ],
"oclc_numbers": [ "417795539" ],
"works": [ { "key": "/works/OL52266W" } ],
"type": { "key": "/type/edition" },
"subjects": [ "Language readers" ],
"local_id": [ "urn:bwbsku:KO-352-184", "urn:bwbsku:UE-795-266" ],
"latest_revision": 9,
"revision": 9,
"created": {
"type": "/type/datetime",
"value": "2008-04-30T09:38:13.731961"
},
"last_modified": {
"type": "/type/datetime",
"value": "2023-01-14T23:55:47.329525"
}
}

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

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

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save