#145 : merge de la branch dev.

This commit is contained in:
Loic Masi
2026-04-03 09:46:13 +02:00
51 changed files with 1118 additions and 315 deletions

View File

@@ -13,7 +13,6 @@ namespace Webzine.Repository
/// <summary>
/// Initialise une classe <see cref="DbArtisteRepository"/> qui implémente l'interface <see cref="IArtisteRepository"/> pour gérer les opérations de base de données liées aux artistes.
/// Utilise <see cref="IArtisteRepository"/> en injection de dépendances.
/// </summary>
public class DbArtisteRepository : IArtisteRepository
{
@@ -23,8 +22,8 @@ namespace Webzine.Repository
/// <summary>
/// Initializes a new instance of the <see cref="DbArtisteRepository"/> class.
/// </summary>
/// <param name="context">Le contexte de base de données à utiliser pour accéder aux entités et effectuer des opérations de
/// persistance. Ne peut pas être null.</param>
/// <param name="context">Le contexte de base de données à utiliser pour accéder aux entités et effectuer des opérations de persistance.</param>
/// <param name="logger">Le service de journalisation.</param>
public DbArtisteRepository(WebzineDbContext context, ILogger<LocalArtisteRepository> logger)
{
this.logger = logger;
@@ -56,11 +55,6 @@ namespace Webzine.Repository
{
try
{
if (artiste == null)
{
throw new ArgumentNullException(nameof(artiste), "L'artiste à supprimer ne peut pas être null.");
}
this.context.Artistes.Remove(artiste);
this.context.SaveChanges();
this.logger.LogDebug("L'artiste {IdArtiste} a bien été supprimé", artiste.IdArtiste);
@@ -84,7 +78,7 @@ namespace Webzine.Repository
{
Artiste artiste = this.context.Artistes
.Include(a => a.Titres)
.FirstOrDefault(a => a.IdArtiste == id);
.SingleOrDefault(a => a.IdArtiste == id);
return artiste;
}
catch (Exception ex)
@@ -118,27 +112,33 @@ namespace Webzine.Repository
try
{
// .AsNoTracking() rend la requête beaucoup plus rapide pour de la lecture
var artistes = this.context.Artistes.AsNoTracking().Include(t => t.Titres).ToList();
this.logger.LogDebug("{Count} artistes récupérés de la base.", artistes.Count);
// Pas besoin de faire un ToList() ici, car on retourne un IEnumerable<Artiste> et EF Core gère l'exécution différée de la requête.
var artistes = this.context.Artistes
.AsNoTracking()
.Include(t => t.Titres);
this.logger.LogDebug("La liste d'artistes a été récupérée de la base.");
return artistes;
}
catch (Exception ex)
{
this.logger.LogError(ex, "Erreur lors de la récupération de tous les artistes.");
return Enumerable.Empty<Artiste>(); // Retourne une liste vide au lieu de faire crash l'UI
throw;
}
}
/// <inheritdoc/>
public void Update(Artiste artiste)
{
if (artiste == null)
{
throw new ArgumentNullException(nameof(artiste));
}
try
{
Artiste existingArtiste = this.Find(artiste.IdArtiste); // Vérifie que l'artiste existe avant de tenter de le mettre à jour
if (existingArtiste == null)
{
this.logger.LogWarning("L'artiste {Id} n'a pas été trouvé pour l'update.", artiste.IdArtiste);
throw new InvalidOperationException($"L'artiste avec l'ID {artiste.IdArtiste} n'a pas été trouvé pour la mise à jour.");
}
this.context.Artistes.Update(artiste);
this.context.SaveChanges();
this.logger.LogDebug("Artiste {Id} ({Nom}) mis à jour avec succès.", artiste.IdArtiste, artiste.Nom);
@@ -167,8 +167,7 @@ namespace Webzine.Repository
var artiste = this.context.Artistes
.Where(a => a.Nom.ToLower().Contains(mot.ToLower()))
.Include(t => t.Titres)
.AsNoTracking()
.ToList();
.AsNoTracking();
return artiste;
}
catch (Exception ex)
@@ -176,5 +175,37 @@ namespace Webzine.Repository
throw new Exception("Erreur lors de la recherche d'artiste {error}", ex);
}
}
/// <inheritdoc/>
public int Count()
{
try
{
int count = Enumerable.Count(this.context.Artistes);
this.logger.LogDebug("Nombre total d'artistes dans la base: {Count}", count);
return count;
}
catch (Exception ex)
{
this.logger.LogError(ex, "Erreur lors du comptage des artistes.");
throw;
}
}
/// <inheritdoc/>
public int Count(Func<Artiste, bool> predicate)
{
try
{
int count = this.context.Artistes.Count(predicate);
this.logger.LogDebug("Nombre d'artistes (avec prédicat): {Count}", count);
return count;
}
catch (Exception ex)
{
this.logger.LogError(ex, "Erreur lors du comptage des artistes avec prédicat.");
throw;
}
}
}
}

View File

@@ -16,10 +16,9 @@ public class DbCommentaireRepository : ICommentaireRepository
private readonly WebzineDbContext context;
/// <summary>
/// Initializes a new instance of the <see cref="DbCommentaireRepository"/> class.
/// Initialisation de <see cref="DbCommentaireRepository"/>.
/// </summary>
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
/// <param name="logger">Le service de journalisation.</param>
/// <param name="context">Le contexte de base de données injecté.</param>
public DbCommentaireRepository(ILogger<DbCommentaireRepository> logger, WebzineDbContext context)
{
@@ -33,7 +32,6 @@ public class DbCommentaireRepository : ICommentaireRepository
{
try
{
this.logger.LogDebug("Ajout d'un nouveau commentaire de l'auteur : {Auteur}", commentaire.Auteur);
this.context.Commentaires.Add(commentaire);
this.context.SaveChanges();
this.logger.LogDebug("Commentaire ajouté avec l'id : {Id}", commentaire.IdCommentaire);
@@ -55,11 +53,6 @@ public class DbCommentaireRepository : ICommentaireRepository
{
try
{
if (commentaire == null)
{
throw new ArgumentNullException(nameof(commentaire), "Le commentaire à supprimer ne peut pas être null.");
}
this.context.Commentaires.Remove(commentaire);
this.context.SaveChanges();
this.logger.LogDebug("Le commentaire {IdCommentaire} a bien été supprimé", commentaire.IdCommentaire);
@@ -76,14 +69,6 @@ public class DbCommentaireRepository : ICommentaireRepository
}
}
/// <inheritdoc/>
public int Count()
{
var count = this.context.Commentaires.Count();
this.logger.LogDebug("Compte total des commentaires : {Count}", count);
return count;
}
/// <inheritdoc/>
public Commentaire Find(int idCommentaire)
{
@@ -92,50 +77,41 @@ public class DbCommentaireRepository : ICommentaireRepository
// On inclut le titre car il est souvent affiché avec le commentaire
return this.context.Commentaires
.Include(c => c.Titre)
.FirstOrDefault(c => c.IdCommentaire == idCommentaire);
.SingleOrDefault(c => c.IdCommentaire == idCommentaire);
}
/// <inheritdoc/>
public IEnumerable<Commentaire> FindAll()
{
this.logger.LogDebug("Récupération de tous les commentaires");
var commentaires = this.context.Commentaires
.AsNoTracking()
.Include(c => c.Titre)
.OrderByDescending(c => c.DateCreation)
.ToList();
.OrderByDescending(c => c.DateCreation);
this.logger.LogDebug("Nombre de commentaires trouvés : {Count}", commentaires.Count);
this.logger.LogDebug("La liste de commentaires a été récupérée.");
return commentaires;
}
/// <inheritdoc/>
public IEnumerable<Commentaire> FindCommentaires(int offset, int limit)
{
this.logger.LogDebug("Recherche paginée des commentaires (offset : {Offset}, limit : {Limit})", offset, limit);
try
{
this.logger.LogDebug("Recherche paginée des commentaires (offset : {Offset}, limit : {Limit})", offset, limit);
var commentaires = this.context.Commentaires
.Include(c => c.Titre)
.OrderByDescending(c => c.DateCreation)
.Skip(offset)
.Take(limit)
.ToList();
var commentaires = this.context.Commentaires
.AsNoTracking()
.Include(c => c.Titre)
.OrderByDescending(c => c.DateCreation)
.Skip(offset)
.Take(limit);
this.logger.LogDebug("{Count} commentaires trouvés pour cette page", commentaires.Count);
return commentaires;
}
/// <inheritdoc/>
public IEnumerable<Commentaire> FindByIdTitre(int idTitre)
{
this.logger.LogDebug("Recherche des commentaires pour le titre ID : {IdTitre}", idTitre);
var commentaires = this.context.Commentaires
.Where(c => c.Titre.IdTitre == idTitre)
.OrderByDescending(c => c.DateCreation)
.ToList();
this.logger.LogDebug($"{commentaires.Count} commentaires trouvés pour l'ID de titre : {idTitre}");
return commentaires;
return commentaires;
}
catch (Exception ex)
{
this.logger.LogError(ex, "Erreur lors de la pagination des commentaires (offset : {Offset}, limit : {Limit})", offset, limit);
throw new Exception("Une erreur est survenue lors de la pagination des commentaires.", ex);
}
}
}

View File

@@ -8,10 +8,17 @@ namespace Webzine.Repository
using Webzine.Entity;
using Webzine.Entity.Fixtures;
/// <summary>
/// Classe de repository pour les entités de la base de données.
/// </summary>
public class DbEntityRepository
{
private readonly WebzineDbContext context;
/// <summary>
/// Constructeur de DbEntityRepository.
/// </summary>
/// <param name="context">DB context.</param>
public DbEntityRepository(WebzineDbContext context)
{
this.context = context;

View File

@@ -93,27 +93,18 @@ public class DbStyleRepository : IStyleRepository
{
this.logger.LogDebug("Recherche du style avec l'ID: {Id}", id);
if (id <= 0)
{
this.logger.LogWarning("Tentative de recherche d'un style avec un Id invalide: {Id}", id);
return new Style();
}
this.logger.LogDebug("Préparation de la requête avec inclusion des titres");
var style = this.context.Styles
.AsNoTracking()
.Include(s => s.Titres)
.FirstOrDefault(s => s.IdStyle == id);
.SingleOrDefault(s => s.IdStyle == id);
if (style == null)
{
this.logger.LogWarning("Style avec l'ID {Id} non trouvé", id);
style = new Style();
}
else
{
this.logger.LogDebug("Style trouvé: {Libelle}", style.Libelle);
return null;
}
this.logger.LogDebug("Style trouvé: {Libelle}", style.Libelle);
return style;
}
catch (Exception ex)
@@ -132,10 +123,10 @@ public class DbStyleRepository : IStyleRepository
this.logger.LogDebug("Tri des styles par libellé");
var styles = this.context.Styles
.OrderBy(s => s.Libelle)
.ToList();
.AsNoTracking()
.OrderBy(s => s.Libelle);
this.logger.LogDebug("{Count} styles récupérés", styles.Count);
this.logger.LogDebug("La liste de styles a été récupérée.");
return styles;
}
catch (Exception ex)
@@ -151,18 +142,15 @@ public class DbStyleRepository : IStyleRepository
try
{
this.logger.LogInformation("Mise à jour du style avec l'ID: {IdStyle}", style.IdStyle);
this.logger.LogDebug("Recherche du style en base de données");
Style existingStyle = this.Find(style.IdStyle); // Vérifie que le style existe avant de tenter de le mettre à jour
var existingStyle = this.context.Styles.Find(style.IdStyle);
if (existingStyle == null)
{
this.logger.LogWarning("Style avec l'ID {IdStyle} non trouvé pour la mise à jour", style.IdStyle);
throw new InvalidOperationException($"Style avec l'ID {style.IdStyle} non trouvé.");
this.logger.LogWarning("Style avec l'ID {IdStyle} non trouvé pour l'update.", style.IdStyle);
throw new InvalidOperationException($"Style avec l'ID {style.IdStyle} non trouvé pour la mise à jour.");
}
// Update properties
this.logger.LogDebug("Style trouvé, mise à jour des propriétés");
existingStyle.Libelle = style.Libelle;
this.context.Styles.Update(style);
this.context.SaveChanges();
this.logger.LogDebug("Style mis à jour avec succès: {IdStyle}", style.IdStyle);
@@ -178,4 +166,20 @@ public class DbStyleRepository : IStyleRepository
throw;
}
}
/// <inheritdoc/>
public int Count()
{
try
{
int count = Enumerable.Count(this.context.Styles);
this.logger.LogDebug("Nombre total de styles: {Count}", count);
return count;
}
catch (Exception ex)
{
this.logger.LogError(ex, "Erreur lors du comptage des styles");
throw;
}
}
}

View File

@@ -192,16 +192,13 @@ public class DbTitreRepository : ITitreRepository
try
{
this.logger.LogInformation("Mise à jour du titre avec l'ID: {IdTitre}", titre.IdTitre);
this.logger.LogDebug("Début de la mise à jour du titre en base de données");
var existingTitre = this.context.Titres.Find(titre.IdTitre);
Titre existingTitre = this.Find(titre.IdTitre);
if (existingTitre != null)
{
this.logger.LogDebug("Titre trouvé, mise à jour des propriétés");
this.context.Entry(existingTitre).CurrentValues.SetValues(titre);
// Handle many-to-many relationships
this.logger.LogDebug("Mise à jour des relations many-to-many (Styles)");
// Relation many-to-many
this.context.Entry(existingTitre).Collection(t => t.Styles).Load();
existingTitre.Styles.Clear();
foreach (var style in titre.Styles)

View File

@@ -10,22 +10,21 @@ namespace Webzine.Repository
using Webzine.Repository.Contracts;
/// <summary>
/// Initialise une classe <see cref="LocalArtisteRepository"/> qui implémente l'interface <see cref="IArtisteRepository"/> pour gérer les opérations de base de données liées aux artistes.
/// Utilise <see cref="IArtisteRepository"/> en injection de dépendances.
/// Initialise une classe <see cref="LocalArtisteRepository"/> qui implémente l'interface <see cref="IArtisteRepository"/>.
/// Gère les opérations liées aux artistes en utilisant une source de données locale (en mémoire).
/// </summary>
public class LocalArtisteRepository : IArtisteRepository
{
private readonly ILogger<LocalArtisteRepository> logger;
// private readonly List<Artiste> artistes;
private readonly InMemoryDataStore dataStore;
/// <summary>
/// Initializes a new instance of the <see cref="LocalArtisteRepository"/> class.
/// Est liéee à une liste d'artistes en local et utilise un logger pour enregistrer les opérations effectuées sur les artistes.
/// </summary>
/// <param name="artistes">La liste des artistes à initialiser. Ne peut pas être null.</param>
/// <param name="logger">Le logger à utiliser pour enregistrer les messages de journalisation. Ne peut pas être null.</param>
/// <param name="dataStore">Le magasin de données en mémoire.</param>
public LocalArtisteRepository(InMemoryDataStore dataStore, ILogger<LocalArtisteRepository> logger)
{
this.logger = logger;
@@ -37,25 +36,19 @@ namespace Webzine.Repository
/// <inheritdoc/>
public void Add(Artiste artiste)
{
throw new NotSupportedException("Mode Local");
this.dataStore.Artistes.Add(artiste);
}
/// <inheritdoc/>
public void Delete(Artiste artiste)
{
throw new NotSupportedException("Mode Local");
this.dataStore.Artistes.Remove(artiste);
}
/// <inheritdoc/>
public Artiste Find(int id)
{
var artiste = this.dataStore.Artistes.First(a => a.IdArtiste == id);
if (artiste == null)
{
return new Artiste();
}
return artiste;
return this.dataStore.Artistes.SingleOrDefault(a => a.IdArtiste == id);
}
/// <inheritdoc/>
@@ -74,7 +67,6 @@ namespace Webzine.Repository
}
/// <inheritdoc/>
/// La liste retournée est une copie de la liste interne, donc elle ne peut être nulle.
public IEnumerable<Artiste> FindAll()
{
return this.dataStore.Artistes;
@@ -83,7 +75,16 @@ namespace Webzine.Repository
/// <inheritdoc/>
public void Update(Artiste artiste)
{
throw new NotSupportedException("Mode Local");
Artiste existingArtiste = this.Find(artiste.IdArtiste);
if (existingArtiste == null)
{
this.logger.LogWarning("L'artiste {Id} n'a pas été trouvé pour l'update.", artiste.IdArtiste);
return;
}
existingArtiste.Nom = artiste.Nom;
existingArtiste.Biographie = artiste.Biographie;
existingArtiste.Titres = artiste.Titres;
}
/// <inheritdoc/>
@@ -93,5 +94,17 @@ namespace Webzine.Repository
.Where(a => a.Nom.ToLower().Contains(mot.ToLower()))
.ToList();
}
/// <inheritdoc/>
public int Count()
{
return this.dataStore.Artistes.Count;
}
/// <inheritdoc/>
public int Count(Func<Artiste, bool> predicate)
{
return this.dataStore.Artistes.Count(predicate);
}
}
}

View File

@@ -4,7 +4,6 @@
namespace Webzine.Repository
{
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,7 +14,6 @@ namespace Webzine.Repository
/// <summary>
/// Initialise une classe <see cref="LocalCommentaireRepository"/> qui implémente l'interface <see cref="ICommentaireRepository"/> pour gérer les opérations liées aux commentaires.
/// Utilise <see cref="ICommentaireRepository"/> en injection de dépendances.
/// </summary>
public class LocalCommentaireRepository : ICommentaireRepository
{
@@ -23,9 +21,8 @@ namespace Webzine.Repository
private readonly InMemoryDataStore dataStore;
/// <summary>
/// Initializes a new instance of the <see cref="LocalCommentaireRepository"/> class.
/// Initialise une nouvelle instance du <see cref="LocalCommentaireRepository"/> .
/// Est liée à un magasin de données en mémoire et utilise un logger pour enregistrer les opérations.
/// Gère les opérations liées aux commentaires en utilisant une source de données locale (en mémoire).
/// </summary>
/// <param name="dataStore">Le magasin de données en mémoire. Ne peut pas être null.</param>
/// <param name="logger">Le logger à utiliser pour enregistrer les messages de journalisation. Ne peut pas être null.</param>
@@ -38,31 +35,19 @@ namespace Webzine.Repository
/// <inheritdoc/>
public void Add(Commentaire commentaire)
{
throw new NotSupportedException("Mode Local");
this.dataStore.Commentaires.Add(commentaire);
}
/// <inheritdoc/>
public void Delete(Commentaire commentaire)
{
throw new NotSupportedException("Mode Local");
}
/// <inheritdoc/>
public int Count()
{
return this.dataStore.Commentaires.Count;
this.dataStore.Commentaires.Remove(commentaire);
}
/// <inheritdoc/>
public Commentaire Find(int idCommentaire)
{
var commentaire = this.dataStore.Commentaires.FirstOrDefault(c => c.IdCommentaire == idCommentaire);
if (commentaire == null)
{
return new Commentaire();
}
return commentaire;
return this.dataStore.Commentaires.SingleOrDefault(c => c.IdCommentaire == idCommentaire);
}
/// <inheritdoc/>
@@ -76,25 +61,10 @@ namespace Webzine.Repository
/// <inheritdoc/>
public IEnumerable<Commentaire> FindCommentaires(int offset, int limit)
{
if (offset < 0 || limit <= 0)
{
return Enumerable.Empty<Commentaire>();
}
return this.dataStore.Commentaires
.OrderByDescending(c => c.DateCreation)
.Skip(offset)
.Take(limit)
.ToList();
}
/// <inheritdoc/>
public IEnumerable<Commentaire> FindByIdTitre(int idTitre)
{
return this.dataStore.Commentaires
.Where(c => c.Titre != null && c.Titre.IdTitre == idTitre)
.OrderByDescending(c => c.DateCreation)
.ToList();
.Take(limit);
}
}
}

View File

@@ -17,9 +17,10 @@ public class LocalStyleRepository : IStyleRepository
/// <summary>
/// Initializes a new instance of the <see cref="LocalStyleRepository"/> class.
/// Gère les opérations liées aux styles en utilisant une source de données locale (en mémoire).
/// </summary>
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
/// <param name="styles">La liste de styles à utiliser comme source de données pour le repository.</param>
/// <param name="dataStore">Les données en mémoire.</param>
public LocalStyleRepository(ILogger<LocalStyleRepository> logger, InMemoryDataStore dataStore)
{
this.logger = logger;
@@ -30,19 +31,19 @@ public class LocalStyleRepository : IStyleRepository
/// <inheritdoc/>
public void Add(Style style)
{
throw new NotSupportedException("Mode local");
this.dataStore.Styles.Add(style);
}
/// <inheritdoc/>
public void Delete(Style style)
{
throw new NotSupportedException("Mode local");
this.dataStore.Styles.Remove(style);
}
/// <inheritdoc/>
public Style Find(int id)
{
return this.dataStore.Styles.Find(s => s.IdStyle == id);
return this.dataStore.Styles.SingleOrDefault(s => s.IdStyle == id);
}
/// <inheritdoc/>
@@ -54,6 +55,20 @@ public class LocalStyleRepository : IStyleRepository
/// <inheritdoc/>
public void Update(Style style)
{
throw new NotSupportedException("Mode local");
Style existingStyle = this.Find(style.IdStyle);
if (existingStyle == null)
{
this.logger.LogWarning("Style with id {IdStyle} not found for update.", style.IdStyle);
return;
}
existingStyle.Libelle = style.Libelle;
existingStyle.Titres = style.Titres;
}
/// <inheritdoc/>
public int Count()
{
return this.dataStore.Styles.Count;
}
}

View File

@@ -28,20 +28,20 @@ public class LocalTitreRepository : ITitreRepository
/// <inheritdoc/>
public void Add(Titre titre)
{
throw new NotSupportedException("Mode local");
this.dataStore.Titres.Add(titre);
}
/// <inheritdoc/>
public int Count()
{
var count = this.dataStore.Titres.Count();
return count;
// On appelle directement LINQ count pour ne pas confondre avec la méthode Count() de l'interface ITitreRepository
return Enumerable.Count(this.dataStore.Titres);
}
/// <inheritdoc/>
public void Delete(Titre titre)
{
throw new NotSupportedException("Mode Local");
this.dataStore.Titres.Remove(titre);
}
/// <inheritdoc/>
@@ -56,27 +56,47 @@ public class LocalTitreRepository : ITitreRepository
/// <inheritdoc/>
public void IncrementNbLectures(Titre titre)
{
titre.NbLectures++;
var stored = this.dataStore.Titres.FirstOrDefault(t => t.IdTitre == titre.IdTitre);
if (stored == null)
{
this.logger.LogWarning("Titre avec l'ID {Id} non trouvé pour incrémenter le nombre de lectures.", titre.IdTitre);
return;
}
stored.NbLectures++;
}
/// <inheritdoc/>
public void IncrementNbLikes(Titre titre)
{
titre.NbLikes++;
var stored = this.dataStore.Titres.FirstOrDefault(t => t.IdTitre == titre.IdTitre);
if (stored == null)
{
this.logger.LogWarning("Titre avec l'ID {Id} non trouvé pour incrémenter le nombre de likes.", titre.IdTitre);
return;
}
stored.NbLikes++;
}
/// <inheritdoc/>
public IEnumerable<Titre> Search(string mot)
{
if (string.IsNullOrWhiteSpace(mot))
{
return Enumerable.Empty<Titre>();
}
return this.dataStore.Titres
.Where(t => t.Libelle != null && t.Libelle.Contains(mot));
.Where(t => t.Libelle.ToLower().Contains(mot.ToLower()))
.ToList();
}
/// <inheritdoc/>
public Titre Find(int idTitre)
{
return this.dataStore.Titres
.First(t => t.IdTitre == idTitre);
.SingleOrDefault(t => t.IdTitre == idTitre);
}
/// <inheritdoc/>
@@ -95,6 +115,20 @@ public class LocalTitreRepository : ITitreRepository
/// <inheritdoc/>
public void Update(Titre titre)
{
throw new NotSupportedException("Mode local");
// On trouve le titre stocké pour mettre à jour ses propriétés avec la méthode Find du repository
// pour éviter la duplication de code.
Titre existingTitre = this.Find(titre.IdTitre);
if (existingTitre == null)
{
this.logger.LogWarning("Titre avec l'ID {Id} non trouvé pour mise à jour.", titre.IdTitre);
return;
}
existingTitre.Libelle = titre.Libelle;
existingTitre.DateCreation = titre.DateCreation;
existingTitre.NbLectures = titre.NbLectures;
existingTitre.NbLikes = titre.NbLikes;
existingTitre.IdArtiste = titre.IdArtiste;
existingTitre.Styles = titre.Styles;
}
}