Compare commits

..

No commits in common. 'master' and 'Front-End' have entirely different histories.

@ -15,13 +15,13 @@ steps:
commands:
- dotnet tool install -g CSharpier # Installez CSharpier si necessaire
- export PATH="$PATH:/root/.dotnet/tools" # Ajoutez le chemin des outils .NET Core au PATH
- cd Sources/
- cd src/
- dotnet-csharpier --check . # Verifie le formatage
- name: build
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dotnet7-maui:latest
commandes:
- cd Sources/
- cd src/
- dotnet restore BookApp.sln
- dotnet build BookApp.sln -c Release --no-restore /p:AndroidSdkDirectory=$ANDROID_SDK_ROOT -property:Aapt2ToolPath=$ANDROID_SDK_ROOT/build-tools/33.0.0
- dotnet publish BookApp/BookApp.csproj -c Release --no-restore -o $CI_PROJECT_DIR/build/release -f:net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk
@ -29,7 +29,7 @@ steps:
- name: test
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dotnet7-maui:latest
commands:
- cd Sources/
- cd src/
- dotnet restore BookApp.sln
- dotnet test BookApp.sln --no-restore /p:AndroidSdkDirectory=$ANDROID_SDK_ROOT -property:Aapt2ToolPath=$ANDROID_SDK_ROOT/build-tools/33.0.0
depends_on: [build]
@ -44,7 +44,7 @@ steps:
project_key: BookApp
coverage_exclusions: "Tests/**"
commands:
- cd Sources/
- cd src/
- dotnet restore BookApp.sln
- dotnet sonarscanner begin /k:$${project_key} /d:sonar.host.url=$${sonar_host} /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml" /d:sonar.coverage.exclusions=$${coverage_exclusions} /d:sonar.login=$${sonar_token}
- dotnet build BookApp.sln -c Release --no-restore /p:AndroidSdkDirectory=$ANDROID_SDK_ROOT -property:Aapt2ToolPath=$ANDROID_SDK_ROOT/build-tools/33.0.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@ -1,40 +0,0 @@
<mxfile host="app.diagrams.net" modified="2023-09-20T12:17:57.703Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0" etag="sVb7Rw98gNrLW8AO19HB" version="21.7.5" type="device">
<diagram name="Page-1" id="e78aO4FO6e_sPu6jAY4w">
<mxGraphModel dx="2261" dy="726" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="XgXC638uMGpcY1R6JfbD-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="XgXC638uMGpcY1R6JfbD-1" target="XgXC638uMGpcY1R6JfbD-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-1" value="MAUI" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="220" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-2" value="&lt;div align=&quot;left&quot;&gt;Contient:&lt;br&gt;- Pages/Vues&lt;/div&gt;&lt;div align=&quot;left&quot;&gt;- VM applicative&lt;br&gt;&lt;/div&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="220" y="440" width="250" height="100" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-3" value="Model" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-110" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="XgXC638uMGpcY1R6JfbD-4" target="XgXC638uMGpcY1R6JfbD-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-4" value="Stub" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-110" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-5" value="WebService" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="70" y="220" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="XgXC638uMGpcY1R6JfbD-6" target="XgXC638uMGpcY1R6JfbD-3">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-6" value="VMWrapper" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-290" y="370" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XgXC638uMGpcY1R6JfbD-7" value="&lt;div align=&quot;left&quot;&gt;Contient:&lt;br&gt;- Interface (pour récup les data)&lt;br&gt;&lt;/div&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="-120" y="440" width="250" height="100" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

@ -874,7 +874,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = ../../Sources
INPUT = ../../src
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

@ -1,195 +1,29 @@
# PocketBook - Louis DUFOUR
# PocketBook
## Informations projet
Application développer sous android API 28 pour ma part.
# TP1 : Pages & Views
_à terminer pour le 17 septembre 2023 - 23h59_
### Documentation
Un schéma d'architecture est encore en cour de construction.
![Image clique droit](/Documentation/AnalyseSonar.PNG)
*Je n'ai pas pu, vous trouvez dans la liste d'ajouts des permissions voici un screenshot qui résume la qualité de mon code*
### Cas d'erreur connus
- Getsion du tabs qui s'adapte
#### Vu principale
- Button + *(TODO)*
- Button modifier *(TODO)*
#### Vu Tous
- Image par défaut *(TODO prévoire une image pardéfaut)*
- Button Tri *(TODO)*
- Button + *(TODO)*
#### Vu Detail
- Button:
- Déplacer Livre *(TODO)*
- Ajouter à la liste à lire plustard *(TODO)*
- Changer le status *(Marche sur le detail, mais se met pas à jour dans la groupCollection)*
- préter le livre *(TODO)*
#### Vu Emprunt/Emprunteur
- logic du switch button *(Début mis en place, mais pas compléter)*
- Button tri *(TODO)*
- Button + *(TODO)*
#### Vu Filtre
- Peut être mieux optimiser je pense
*ps: je sais pas pk sur mon tel lorsque l'appli est déployer la page ne veut même plus s'afficher. Par contre, lorsque je connecte mon téléphone avec un capable et je l'application via l'IDE cela marche parfaitement bien*
#### Vu Favoris
- pas faite si le passage du detail m'avais pas tant bloquer
### L'accès au code noté
- Projet ToolKitt
- Projet VMWrapper
- Projet BookApp Dossier ViewModel, UseCase, Composants, Pages
## Modèle
```mermaid
classDiagram
direction LR
class Book {
+Id : string
+Title : string
+Publishers : List~string~
+PublishDate : DateTime
+ISBN13 : string
+Series : List~string~
+NbPages : int
+Format : string
+ImageSmall : string
+ImageMedium : string
+ImageLarge : string
}
class Languages {
<<enum>>
Unknown,
French,
}
class Work {
+Id : string
+Description : string
+Title : string
+Subjects : List~string~
}
class Contributor {
+Name : string
+Role : string
}
class Author {
+Id : string
+Name : string
+ImageSmall : string
+ImageMedium : string
+ImageLarge : string
+Bio : string
+AlternateNames : List~string~
+BirthDate : DateTime?
+DeathDate : DateTime?
}
class Link {
+Title : string
+Url : string
}
class Ratings {
+Average : float
+Count : int
}
Book --> "1" Languages : Language
Book --> "*" Contributor : Contributors
Author --> "*" Link : Links
Work --> "*" Author : Authors
Work --> "1" Ratings : Ratings
Book --> "*" Author : Authors
Book --> "*" Work : Works
class Status {
<<enum>>
Unknown,
Finished,
Reading,
NotRead,
ToBeRead
}
class Contact{
+string Id;
+string FirstName;
+string LastName;
}
```
## Services & Interfaces
```mermaid
classDiagram
direction LR
Book --> "1" Languages : Language
Book --> "*" Contributor : Contributors
Author --> "*" Link : Links
Work --> "1" Ratings : Ratings
Work --> "*" Author : Authors
Book --> "*" Work : Works
Book --> "*" Author : Authors
class IUserLibraryManager {
<<interface>>
+AddBook(Book book) : Task<Book>
+AddBook(string id) : Task<Book>
+AddBookByIsbn(string isbn) : Task<Book>
+RemoveBook(Book book) : Task<bool>
+RemoveBook(string id) : Task<bool>
+RemoveBook(string id) : Task<bool>
+AddToFavorites(Book book) : Task<bool>
+AddToFavorites(string bookId) : Task<bool>
+RemoveFromFavorites(Book book) : Task<bool>
+RemoveFromFavorites(string bookId) : Task<bool>
+UpdateBook(Book updatedBook) : Task<Book>
+AddContact(Contact contact) : Task<Contact>
+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<bool>
+GetCurrentLoans(int index, int count)
+GetPastLoans(int index, int count)
+GetCurrentBorrowings(int index, int count)
+GetPastBorrowings(int index, int count)
+GetContacts(int index, int count)
}
class ILibraryManager {
<<interface>>
+GetBookById(string id)
+GetBookByIsbn(string isbn)
+GetBooksByTitle(string title, int index, int count, string sort)
+GetBooksByAuthorId(string authorId, int index, int count, string sort)
+GetBooksByAuthor(string author, int index, int count, string sort)
+GetAuthorById(string id)
+GetAuthorsByName(string substring, int index, int count, string sort)
}
class Status {
<<enum>>
}
IUserLibraryManager ..|> ILibraryManager
IUserLibraryManager ..> Status
IUserLibraryManager ..> Contact
IUserLibraryManager ..> Book
ILibraryManager ..> Book
ILibraryManager ..> Author
```
L'objectif est de reproduire les pages et les vues d'une application _PocketBook_ de recherche et de stockage de livres pour iPhone et Android en MAUI.
Une reproduction la plus _parfaite_ possible est souhaitée (avec la _TabBar_, le bouton _Modifier_, etc.).
Je vous conseille de faire (par ordre de priorité) :
- la page d'accueil de la bibliothèque ("Mes livres" avec les menus),
- la page affichant les livres,
- la page affichant les détails d'un livre,
- la page permettant de lister les auteurs et de filtrer les livres par auteur
- le menu contextuel permettant d'ajouter un nouveau livre
- une page permettant de prêter ou d'emprunter à livre à quelqu'un
- la page permettant de lister les années de publication et de filtrer les livres
- la page permettant de trier les livres par note.
## Critères d'évaluation
- L'app doit marcher sur iOS et Android
- Création des pages
- Utilisation de _Views_ (layout, affichage, buttons...)
- Création de _Views_ personnalisées
- Création et utilisation de Styles et ressources
- Création et utilisation des _Themes_ (light & dark)
- (si besoin) utilisation du MAUI Community Toolkit
## Ressources
- [Doc officielle MAUI](https://learn.microsoft.com/en-us/dotnet/maui/)
@ -237,11 +71,6 @@ Lorsqu'on clique sur un le filtre "Date de publication" de la page "Mes Livres",
Un clic sur une de ces années permet d'aller sur la même page que celle qui affiche la liste des livres, mais seuls les livres publiée cette année apparaitront.
### Emprunts / Prêts
Lorsqu'on clique sur le menu "En prêt" sur la page d'accueil, on peut voir la liste de nos emprunts et de nos prêts répartis par contacts. Par exemple :
<img src="screens/emprunts.png" width="300"/>
Le switch permet de voir les livres qu'on a prêtés regroupés par contacts, ou les livres qu'on a empruntés regroupés par prêteurs.
### Le fameux bouton "+"
Un clic sur le bouton "+" fait apparaître un sous-menu permettant d'ajouter un livre à la bibliothèque :
- en scannant un code-barres (cf. plus bas)
@ -256,3 +85,11 @@ La page Scan pourra être réalisée plus tard.
<img src="screens/Scan.jpeg" width="300"/>
# Troubleshootings
#### J'ai bien renommé mes images, mais le Mac continue à me dire que non...
C'est peut-être dû au fait que certains fichiers cachés n'ont pas été correctement exclus (notamment les ```.DS_Store```).
Essayez d'appeler cette commande depuis un Terminal (sous Visual ou depuis... un Terminal) :
```
find . -name ".DS_Store" -delete
```

@ -1,87 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34024.191
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BookApp", "BookApp\BookApp.csproj", "{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DtoAbstractLayer", "DtoAbstractLayer\DtoAbstractLayer.csproj", "{C45DF3F7-AFCA-4252-89CC-1A6E5CA9EF33}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonReader", "JsonReader\JsonReader.csproj", "{F2A0B67A-2BA9-4447-9BFD-708D430EBCFA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryDTO", "LibraryDTO\LibraryDTO.csproj", "{94F9B003-9A66-424E-9758-77D140F041BA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{70ECEB25-24E4-47AA-BA64-0F0C8C4F090F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubbedDTO", "StubbedDTO\StubbedDTO.csproj", "{B84474A1-D290-473B-AFF3-FA4ECB509024}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "Utils\Utils.csproj", "{03A3193F-92CC-4360-8061-E5207CC7FD2F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stub", "Stub\Stub.csproj", "{14ED7ADF-7B83-432E-A87B-40C153041067}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{945A6DB3-CB0A-4FB8-8117-64F0CB0D838A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VMWrapper", "VMWrapper\VMWrapper.csproj", "{377DC5AB-5D7C-4A29-ACED-C08B581A8E58}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToolKit", "ToolKit\ToolKit.csproj", "{778541B9-BECF-49DC-8860-B8F134A6F4E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}.Release|Any CPU.Build.0 = Release|Any CPU
{DBD6BB08-83E2-42EA-A28C-32DB6959E4B5}.Release|Any CPU.Deploy.0 = Release|Any CPU
{C45DF3F7-AFCA-4252-89CC-1A6E5CA9EF33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C45DF3F7-AFCA-4252-89CC-1A6E5CA9EF33}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C45DF3F7-AFCA-4252-89CC-1A6E5CA9EF33}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C45DF3F7-AFCA-4252-89CC-1A6E5CA9EF33}.Release|Any CPU.Build.0 = Release|Any CPU
{F2A0B67A-2BA9-4447-9BFD-708D430EBCFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2A0B67A-2BA9-4447-9BFD-708D430EBCFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2A0B67A-2BA9-4447-9BFD-708D430EBCFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2A0B67A-2BA9-4447-9BFD-708D430EBCFA}.Release|Any CPU.Build.0 = Release|Any CPU
{94F9B003-9A66-424E-9758-77D140F041BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94F9B003-9A66-424E-9758-77D140F041BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94F9B003-9A66-424E-9758-77D140F041BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94F9B003-9A66-424E-9758-77D140F041BA}.Release|Any CPU.Build.0 = Release|Any CPU
{70ECEB25-24E4-47AA-BA64-0F0C8C4F090F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70ECEB25-24E4-47AA-BA64-0F0C8C4F090F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70ECEB25-24E4-47AA-BA64-0F0C8C4F090F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70ECEB25-24E4-47AA-BA64-0F0C8C4F090F}.Release|Any CPU.Build.0 = Release|Any CPU
{B84474A1-D290-473B-AFF3-FA4ECB509024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B84474A1-D290-473B-AFF3-FA4ECB509024}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B84474A1-D290-473B-AFF3-FA4ECB509024}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B84474A1-D290-473B-AFF3-FA4ECB509024}.Release|Any CPU.Build.0 = Release|Any CPU
{03A3193F-92CC-4360-8061-E5207CC7FD2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03A3193F-92CC-4360-8061-E5207CC7FD2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03A3193F-92CC-4360-8061-E5207CC7FD2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03A3193F-92CC-4360-8061-E5207CC7FD2F}.Release|Any CPU.Build.0 = Release|Any CPU
{14ED7ADF-7B83-432E-A87B-40C153041067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14ED7ADF-7B83-432E-A87B-40C153041067}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14ED7ADF-7B83-432E-A87B-40C153041067}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14ED7ADF-7B83-432E-A87B-40C153041067}.Release|Any CPU.Build.0 = Release|Any CPU
{945A6DB3-CB0A-4FB8-8117-64F0CB0D838A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{945A6DB3-CB0A-4FB8-8117-64F0CB0D838A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{945A6DB3-CB0A-4FB8-8117-64F0CB0D838A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{945A6DB3-CB0A-4FB8-8117-64F0CB0D838A}.Release|Any CPU.Build.0 = Release|Any CPU
{377DC5AB-5D7C-4A29-ACED-C08B581A8E58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{377DC5AB-5D7C-4A29-ACED-C08B581A8E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{377DC5AB-5D7C-4A29-ACED-C08B581A8E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{377DC5AB-5D7C-4A29-ACED-C08B581A8E58}.Release|Any CPU.Build.0 = Release|Any CPU
{778541B9-BECF-49DC-8860-B8F134A6F4E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{778541B9-BECF-49DC-8860-B8F134A6F4E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{778541B9-BECF-49DC-8860-B8F134A6F4E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{778541B9-BECF-49DC-8860-B8F134A6F4E2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {27FCF0C7-3613-4FC6-9E95-2EEFB171F0F4}
EndGlobalSection
EndGlobal

@ -1,19 +0,0 @@
using BookApp.ViewModel;
using VMWrapper;
namespace BookApp.Composants
{
public partial class CollectionFiltrage : ContentView
{
public CollectionFiltrage()
{
InitializeComponent();
}
public CollectionFiltrage(FilterViewModel data)
: this()
{
BindingContext = data;
}
}
}

@ -1,84 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:composants="clr-namespace:BookApp.Composants"
xmlns:model="clr-namespace:Model;assembly=Model"
xmlns:usecase="clr-namespace:BookApp.UseCase"
x:Name="ThisPage"
x:Class="BookApp.Composants.GroupCollection">
<ContentView.Resources>
<ResourceDictionary>
<usecase:AuthorsListToStringConverter x:Key="authorsConverter"/>
</ResourceDictionary>
</ContentView.Resources>
<CollectionView ItemsSource="{Binding AuteurGroups}" IsGrouped="True" SelectionMode="Single" x:Name="groupCollectionView">
<CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="model:Author">
<Label Text="{Binding Name}"
FontFamily="SF-Compact-Display-Semibold"
BackgroundColor="LightGrey"
TextColor="Gray"
Padding="5"/>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Book">
<SwipeView Style="{StaticResource StyleCollectionStack}">
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem Text="Favorite"
IconImageSource="favorite.png"
BackgroundColor="LightGreen"/>
<SwipeItem Text="Delete"
IconImageSource="delete.png"
BackgroundColor="LightPink"
Command="{Binding Source={x:Reference groupCollectionView}, Path=BindingContext.RemoveBookCommand}"
CommandParameter="{Binding Id}"/>
</SwipeItems>
</SwipeView.LeftItems>
<StackLayout>
<Grid>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference ThisPage}, Path=Nav.BookDetailCommand}"
CommandParameter="{Binding Id}"/>
</Grid.GestureRecognizers>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Source="{Binding ImageLarge}"
HeightRequest="125"
WidthRequest="125"
Margin="0,10,0,10"/>
<StackLayout Grid.Column="1">
<Label Text="{Binding Title}"
FontFamily="SF-Compact-Display-Bold"
FontSize="18"
TextColor="Black"/>
<Label Text="{Binding Authors, Converter={StaticResource authorsConverter}}" FontFamily="SF-Compact-Display-Semibold" TextColor="Black"/>
<Label
TextColor="SlateGray"
Text="{Binding Status}"/>
<StackLayout Margin="0,10" Orientation="Horizontal" VerticalOptions="EndAndExpand">
<Image Source="empty_star.svg" WidthRequest="25" HeightRequest="25"/>
<Image Source="empty_star.svg" WidthRequest="25" HeightRequest="25"/>
<Image Source="empty_star.svg" WidthRequest="25" HeightRequest="25"/>
<Image Source="empty_star.svg" WidthRequest="25" HeightRequest="25"/>
<Image Source="empty_star.svg" WidthRequest="25" HeightRequest="25"/>
</StackLayout>
</StackLayout>
</Grid>
<Rectangle Margin="25,0,0,10" HeightRequest="1" BackgroundColor="LightGray" VerticalOptions="End" />
</StackLayout>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentView>

@ -1,21 +0,0 @@
using BookApp.ViewModel;
using VMWrapper;
namespace BookApp.Composants
{
public partial class GroupCollection : ContentView
{
public ViewModelNavigation Nav { get; } = new ViewModelNavigation();
public GroupCollection()
{
InitializeComponent();
}
public GroupCollection(BooksViewModel data)
: this()
{
BindingContext = data;
}
}
}

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookApp.Composants.ToggleSwitchView">
<Grid
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout
Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Center"
Grid.Row="0"
Spacing="30">
<Label
x:Name="OffLabel"
Text="Prêts"
Opacity="0.25"
VerticalTextAlignment="Center"/>
<Grid
VerticalOptions="Center"
HorizontalOptions="Center"
Scale="1.5">
<Switch
x:Name="ToggleSwitch"
Toggled="OnToggled" />
</Grid>
<Label
x:Name="OnLabel"
Text="Emprunts"
Opacity="0.25"
VerticalTextAlignment="Center"/>
</StackLayout>
<!-- Autres éléments de l'interface utilisateur si nécessaire -->
</Grid>
</ContentView>

@ -1,24 +0,0 @@
namespace BookApp.Composants;
public partial class ToggleSwitchView : ContentView
{
public ToggleSwitchView()
{
InitializeComponent();
}
// Gestionnaire d'<27>v<EFBFBD>nements pour le changement d'<27>tat du switch
private void OnToggled(object sender, ToggledEventArgs e)
{
if (e.Value) // Si c'est ON
{
OnLabel.Opacity = 1;
OffLabel.Opacity = 0.25;
}
else // Si c'est OFF
{
OnLabel.Opacity = 0.25;
OffLabel.Opacity = 1;
}
}
}

@ -1,18 +0,0 @@
using BookApp.Pages;
using System.Collections.ObjectModel;
using BookApp.ViewModel;
using System.Diagnostics;
namespace BookApp
{
public partial class MainPage : ContentPage
{
public ViewModelNavigation Nav { get; } = new ViewModelNavigation();
public MainPage(ViewModelMenu data)
{
InitializeComponent();
BindingContext = data;
}
}
}

@ -1,52 +0,0 @@
using BookApp.Composants;
using BookApp.Pages;
using BookApp.ViewModel;
using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;
using Model;
using SimpleRatingControlMaui;
using StubLib;
using VMWrapper;
namespace BookApp
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.UseSimpleRatingControl()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
builder.Services.AddSingleton<ILibraryManager, LibraryStub>();
builder.Services.AddSingleton<IUserLibraryManager, UserLibraryStub>();
builder.Services.AddSingleton<BooksViewModel>();
builder.Services.AddSingleton<FilterViewModel>();
builder.Services.AddSingleton<DetailBookViewModel>();
builder.Services.AddSingleton<ViewModelDetailProvider>();
builder.Services.AddSingleton<ViewModelNavigation>();
builder.Services.AddSingleton<ViewModelMenu>();
builder.Services.AddSingleton<DetailBook>();
builder.Services.AddSingleton<GroupCollection>();
builder.Services.AddSingleton<CollectionFiltrage>();
builder.Services.AddSingleton<Filtrage>();
builder.Services.AddSingleton<Tous>();
builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<EmpruntsPrets>();
return builder.Build();
}
}
}

@ -1,16 +0,0 @@
using BookApp.ViewModel;
using System.Diagnostics;
namespace BookApp.Pages
{
public partial class DetailBook : ContentPage
{
public ViewModelNavigation Nav { get; } = new ViewModelNavigation();
public DetailBook(ViewModelDetailProvider VMProvider)
{
InitializeComponent();
BindingContext = VMProvider;
}
}
}

@ -1,17 +0,0 @@
using BookApp.ViewModel;
using System.Windows.Input;
using VMWrapper;
namespace BookApp.Pages
{
public partial class EmpruntsPrets : ContentPage
{
public ViewModelNavigation Nav { get; } = new ViewModelNavigation();
public EmpruntsPrets(BooksViewModel data)
{
InitializeComponent();
BindingContext = data;
}
}
}

@ -1,20 +0,0 @@
using BookApp.ViewModel;
using System.Diagnostics;
using VMWrapper;
namespace BookApp.Pages
{
[QueryProperty(nameof(Title), "Title")]
public partial class Filtrage : ContentPage
{
private readonly string Title;
public ViewModelNavigation Nav { get; } = new ViewModelNavigation();
public Filtrage(FilterViewModel data)
{
InitializeComponent();
BindingContext = data;
}
}
}

@ -1,18 +0,0 @@
using BookApp.ViewModel;
using System.Collections.ObjectModel;
using System.Diagnostics;
using VMWrapper;
namespace BookApp.Pages
{
public partial class Tous : ContentPage
{
public ViewModelNavigation Nav { get; } = new ViewModelNavigation();
public Tous(BooksViewModel data)
{
InitializeComponent();
BindingContext = data;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -1,33 +0,0 @@
using Model;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookApp.UseCase
{
public class AuthorsListToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is List<Author> authorsList && authorsList.Any())
{
return string.Join(", ", authorsList.Select(a => a.Name));
}
return string.Empty;
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture
)
{
throw new NotImplementedException();
}
}
}

@ -1,125 +0,0 @@
using Model;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Input;
using ToolKit;
using VMWrapper;
namespace BookApp.ViewModel
{
[QueryProperty(nameof(BookId), "BookId")]
public class ViewModelDetailProvider : BaseViewModel
{
private readonly DetailBookViewModel _detailBookViewModel;
private string _id;
public string BookId
{
get => _id;
set
{
if (_id != value)
{
_id = value;
OnPropertyChanged();
LoadBookDetailAsync(_id);
}
}
}
private Book _bookDetail;
public Book BookDetail
{
get => _bookDetail;
set
{
_bookDetail = value;
OnPropertyChanged();
}
}
// Command Pop-up
public ICommand _popUpCommand;
public ICommand PopUpCommand => _popUpCommand ??= new CommandPersonnal(ShowPopUP);
public ViewModelDetailProvider(DetailBookViewModel detailBookViewModel)
{
_detailBookViewModel =
detailBookViewModel ?? throw new ArgumentNullException(nameof(detailBookViewModel));
}
private async void ShowPopUP()
{
string action = await DisplayActionSheet(
"Quel est le statut du livre ?",
"Cancel",
null,
"Finished",
"Reading",
"NotRead",
"ToBeRead"
);
switch (action)
{
case "Finished":
BookDetail.Status = Status.Finished;
OnPropertyChanged(nameof(BookDetail));
_detailBookViewModel.UpdateColleciton();
break;
case "Reading":
BookDetail.Status = Status.Reading;
OnPropertyChanged(nameof(BookDetail));
_detailBookViewModel.UpdateColleciton();
break;
case "NotRead":
BookDetail.Status = Status.NotRead;
OnPropertyChanged(nameof(BookDetail));
_detailBookViewModel.UpdateColleciton();
break;
case "ToBeRead":
BookDetail.Status = Status.ToBeRead;
OnPropertyChanged(nameof(BookDetail));
_detailBookViewModel.UpdateColleciton();
break;
default:
BookDetail.Status = Status.Unknown;
OnPropertyChanged(nameof(BookDetail));
break;
}
}
public async Task<string> DisplayActionSheet(
string title,
string cancel,
string destruction,
params string[] buttons
)
{
return await Application.Current.MainPage.DisplayActionSheet(
title,
cancel,
destruction,
buttons
);
}
private async Task LoadBookDetailAsync(string bookId)
{
if (string.IsNullOrWhiteSpace(_id))
return;
try
{
await _detailBookViewModel.LoadBookDetail(bookId);
BookDetail = _detailBookViewModel.BookDetail;
}
catch (Exception ex)
{
Debug.WriteLine($"An error occurred: {ex.Message}");
}
}
}
}

@ -1,67 +0,0 @@
using System.Collections.ObjectModel;
using VMWrapper;
namespace BookApp.ViewModel
{
public class ViewModelMenu
{
public ObservableCollection<ViewModelMenuItem> MenuItemsLivre { get; set; }
public ObservableCollection<ViewModelMenuItem> MenuItemsFiltre { get; set; }
public ViewModelMenu(BooksViewModel booksViewModel)
{
MenuItemsLivre = new ObservableCollection<ViewModelMenuItem>()
{
new ViewModelMenuItem(
"Tous",
"../Resources/Images/tray_2_fill.svg",
booksViewModel.TotalBooks,
"TousPage"
),
new ViewModelMenuItem(
"En prêt",
"../Resources/Images/person_badge_clock_fill.svg",
null,
"EmpruntsPretsPage"
),
new ViewModelMenuItem(
"À lire plus tard",
"../Resources/Images/arrow_forward.svg",
null,
""
),
new ViewModelMenuItem(
"Statut de lecture",
"../Resources/Images/eyeglasses.svg",
null,
""
),
new ViewModelMenuItem("Favoris", "../Resources/Images/heart_fill.svg", null, ""),
new ViewModelMenuItem(
"Étiquettes",
"../Resources/Images/tag_fill.svg",
null,
"",
true
),
};
MenuItemsFiltre = new ObservableCollection<ViewModelMenuItem>()
{
new ViewModelMenuItem(
"Auteur",
"../Resources/Images/person_fill.svg",
booksViewModel.TotalAuthors,
"FiltragePage"
),
new ViewModelMenuItem(
"Date de publication",
"../Resources/Images/calendar.svg",
null,
"FiltragePage"
),
new ViewModelMenuItem("Note", "../Resources/Images/sparkles.svg", null, "", true),
};
}
}
}

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookApp.ViewModel
{
public class ViewModelMenuItem
{
public string Name { get; set; }
public string Icone { get; set; }
public long? Number { get; set; }
public string Route { get; set; }
public bool IsLastItem { get; set; }
public ViewModelMenuItem(
string name,
string icone,
long? number,
string route,
bool isLastItem = false
)
{
Name = name;
Icone = icone;
Number = number;
Route = route;
IsLastItem = isLastItem;
}
}
}

@ -1,115 +0,0 @@
using ToolKit;
using System.Windows.Input;
using System.Diagnostics;
using Model;
using System.Net;
namespace BookApp.ViewModel
{
public class ViewModelNavigation : BaseViewModel
{
public INavigation Navigation { get; set; }
private string _title;
public string Title
{
get => _title;
set
{
_title = value;
OnPropertyChanged();
}
}
// Command Pop
public ICommand _backButtonCommand;
public ICommand BackButtonCommand =>
_backButtonCommand ??= new CommandPersonnal(
async () => await BackButton(this, EventArgs.Empty)
);
// Command Collection livre
public ICommand _bookDetailCommand;
public ICommand BookDetailCommand =>
_bookDetailCommand ??= new CommandPersonnal<string>(
async (book) => await OnToItemDetail(book)
);
// Command Collection menu
private ICommand _menuItemsCommand;
public ICommand MenuItemsCommand =>
_menuItemsCommand ??= new CommandPersonnal<ViewModelMenuItem>(
async (item) => await OnItemSelected(item)
);
public ICommand _menuItemsFiltreCommand;
public ICommand MenuItemsFiltreCommand =>
_menuItemsFiltreCommand ??= new CommandPersonnal<ViewModelMenuItem>(
async (item) => await OnItemSelected(item)
);
public ViewModelNavigation() { }
private async Task OnItemSelected(ViewModelMenuItem selectedItem)
{
if (string.IsNullOrEmpty(selectedItem?.Route))
{
Debug.WriteLine("Route is null, cannot navigate.");
return;
}
try
{
Title = selectedItem.Name;
SelectTab(selectedItem.Route);
var navigationParameter = new Dictionary<string, object> { { "Title", Title } };
await Shell.Current.GoToAsync(selectedItem.Route, navigationParameter);
Shell.Current.ForceLayout();
}
catch (Exception ex)
{
Debug.WriteLine($"Navigation failed: {ex.Message}");
}
}
private async Task OnToItemDetail(string bookId)
{
if (bookId == null)
{
Debug.WriteLine("Book is null, cannot navigate to details page.");
return;
}
try
{
var navigationParameter = new Dictionary<string, object> { { "BookId", bookId } };
await Shell.Current.GoToAsync($"DetailBookPage", navigationParameter);
}
catch (Exception ex)
{
Debug.WriteLine($"Navigation failed: {ex.Message}");
}
}
async Task BackButton(object sender, EventArgs args)
{
if (Shell.Current.Navigation.NavigationStack.Count > 1)
{
await Shell.Current.Navigation.PopAsync();
}
}
private void SelectTab(string route)
{
var shellItem = Shell.Current.Items.FirstOrDefault(
item => item.Items.Any(section => section.Route.Contains(route))
);
if (shellItem != null)
{
Shell.Current.CurrentItem = shellItem;
}
}
}
}

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

@ -1,116 +0,0 @@
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 = ""
);
}

@ -1,103 +0,0 @@
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);
}
}
}

@ -1,117 +0,0 @@
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);
}
}

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

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

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

@ -1,23 +0,0 @@
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>();
}
}

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

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

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

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

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

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

@ -1,36 +0,0 @@
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();
}
}

@ -1,41 +0,0 @@
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();
}

@ -1,29 +0,0 @@
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();
}
}

@ -1,26 +0,0 @@
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();
}
}

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

@ -1,35 +0,0 @@
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 = ""
);
}
}

@ -1,43 +0,0 @@
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);
}
}

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

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

@ -1,28 +0,0 @@
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();
}
}

@ -1,133 +0,0 @@
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));
}
}
}

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

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

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

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

@ -1,29 +0,0 @@
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,49 +0,0 @@
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);
}
}

@ -1,98 +0,0 @@
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());
}
}

@ -1,68 +0,0 @@
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());
}
}

@ -1,24 +0,0 @@
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();
}
}
}

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

@ -1,543 +0,0 @@
using System;
using System.Collections.ObjectModel;
using Model;
using System.Linq;
using LibraryDTO;
using System.Xml.Linq;
using System.Diagnostics;
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))
);
}
}
}

@ -1,270 +0,0 @@
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);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,13 +0,0 @@
{
"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