diff --git a/Webzine.Business/DashboardService.cs b/Webzine.Business/DashboardService.cs index f1b566d..f68d1d5 100644 --- a/Webzine.Business/DashboardService.cs +++ b/Webzine.Business/DashboardService.cs @@ -2,7 +2,6 @@ namespace Webzine.Business; using Webzine.Business.Contracts; using Webzine.Business.Contracts.Dto; -using Webzine.Entity; using Webzine.Repository.Contracts; /// @@ -34,37 +33,22 @@ public class DashboardService : IDashboardService /// public DashboardDTO GetDashboardData() { - IEnumerable titres = this.titreRepository.FindAll(); - - Artiste? artisteLePlusChronique = titres - .GroupBy(t => t.Artiste) - .OrderByDescending(g => g.Count()) - .FirstOrDefault() - ?.Key; - - Artiste? albumLePlusChronique = titres - .GroupBy(t => (t.Artiste, t.Album)) - .GroupBy(g => g.Key.Artiste) - .OrderByDescending(g => g.Count()) - .FirstOrDefault() - ?.Key; - - Titre? musiqueLaPlusJouee = titres - .OrderByDescending(t => t.NbLectures) - .FirstOrDefault(); + string artisteLePlusChronique = this.titreRepository.FindMostReviewedArtistName() ?? string.Empty; + string albumLePlusChronique = this.titreRepository.FindArtistNameWithMostReviewedAlbums() ?? string.Empty; + var musiqueLaPlusJouee = this.titreRepository.FindMostPlayedTitle(); return new DashboardDTO { NombreArtistes = this.artisteRepository.Count(), - ArtisteLePlusChronique = artisteLePlusChronique.Nom, - AlbumLePlusChronique = albumLePlusChronique.Nom, - NombreBiographies = this.artisteRepository.Count(a => !string.IsNullOrEmpty(a.Biographie)), - IdMusiqueLaPlusJouee = musiqueLaPlusJouee.IdTitre, - MusiqueLaPlusJouee = musiqueLaPlusJouee.Libelle, + ArtisteLePlusChronique = artisteLePlusChronique, + AlbumLePlusChronique = albumLePlusChronique, + NombreBiographies = this.artisteRepository.CountWithBiography(), + IdMusiqueLaPlusJouee = musiqueLaPlusJouee?.IdTitre ?? 0, + MusiqueLaPlusJouee = musiqueLaPlusJouee?.Libelle ?? string.Empty, NombreTitres = this.titreRepository.Count(), NombreGenres = this.styleRepository.Count(), - NombreLectures = titres.Sum(t => t.NbLectures), - NombreLikes = titres.Sum(t => t.NbLikes), + NombreLectures = this.titreRepository.CountLecture(), + NombreLikes = this.titreRepository.CountLike(), }; } } \ No newline at end of file diff --git a/Webzine.Repository.Contracts/IArtisteRepository.cs b/Webzine.Repository.Contracts/IArtisteRepository.cs index b480816..1f9637e 100644 --- a/Webzine.Repository.Contracts/IArtisteRepository.cs +++ b/Webzine.Repository.Contracts/IArtisteRepository.cs @@ -72,5 +72,11 @@ namespace Webzine.Repository.Contracts /// Le prédicat de filtrage. /// Le nombre d'artistes correspondants. int Count(Func predicate); + + /// + /// Récupère le nombre d'artistes ayant une biographie renseignée. + /// + /// Le nombre d'artistes avec biographie. + int CountWithBiography(); } } \ No newline at end of file diff --git a/Webzine.Repository.Contracts/ITitreRepository.cs b/Webzine.Repository.Contracts/ITitreRepository.cs index 92427aa..8c574c1 100644 --- a/Webzine.Repository.Contracts/ITitreRepository.cs +++ b/Webzine.Repository.Contracts/ITitreRepository.cs @@ -77,5 +77,35 @@ /// /// L'objet titre à mettre à jour. void Update(Titre titre); + + /// + /// Retourne le nombre total de likes. + /// + /// Integer. + int CountLike(); + + /// + /// Retourne le nombre total de lecture. + /// + /// Integer. + int CountLecture(); + + /// + /// Retourne le nom de l'artiste ayant le plus de titres chroniqués. + /// + /// Le nom de l'artiste le plus chroniqué, ou null si aucun titre n'existe. + string? FindMostReviewedArtistName(); + + /// + /// Retourne le nom de l'artiste ayant le plus d'albums chroniqués. + /// + /// Le nom de l'artiste concerné, ou null si aucun titre n'existe. + string? FindArtistNameWithMostReviewedAlbums(); + + /// + /// Retourne l'identifiant et le libellé du titre le plus joué. + /// + /// Un tuple contenant l'identifiant et le libellé du titre le plus joué, ou null si aucun titre n'existe. + (int IdTitre, string Libelle)? FindMostPlayedTitle(); } } \ No newline at end of file diff --git a/Webzine.Repository/DbArtisteRepository.cs b/Webzine.Repository/DbArtisteRepository.cs index 0692fbb..98bb8d2 100644 --- a/Webzine.Repository/DbArtisteRepository.cs +++ b/Webzine.Repository/DbArtisteRepository.cs @@ -208,6 +208,22 @@ namespace Webzine.Repository } } + /// + public int CountWithBiography() + { + try + { + int count = this.context.Artistes.Count(a => !string.IsNullOrEmpty(a.Biographie)); + this.logger.LogDebug("Nombre d'artistes avec biographie: {Count}", count); + return count; + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors du comptage des artistes avec biographie."); + throw; + } + } + /// public IEnumerable FindArtistes(int offset, int limit) { diff --git a/Webzine.Repository/DbTitreRepository.cs b/Webzine.Repository/DbTitreRepository.cs index 918d543..37ecdb0 100644 --- a/Webzine.Repository/DbTitreRepository.cs +++ b/Webzine.Repository/DbTitreRepository.cs @@ -33,7 +33,6 @@ public class DbTitreRepository : ITitreRepository try { this.logger.LogInformation("Ajout d'un nouveau titre: {Libelle}", titre.Libelle); - this.logger.LogDebug("Début de l'ajout du titre en base de données"); this.context.Titres.Add(titre); this.context.SaveChanges(); @@ -57,7 +56,6 @@ public class DbTitreRepository : ITitreRepository { try { - this.logger.LogDebug("Comptage des titres en base de données"); var count = this.context.Titres.Count(); this.logger.LogDebug("Nombre total de titres: {Count}", count); return count; @@ -75,7 +73,6 @@ public class DbTitreRepository : ITitreRepository try { this.logger.LogInformation("Suppression du titre avec l'ID: {IdTitre}", titre.IdTitre); - this.logger.LogDebug("Début de la suppression du titre en base de données"); this.context.Titres.Remove(titre); this.context.SaveChanges(); @@ -100,7 +97,6 @@ public class DbTitreRepository : ITitreRepository try { this.logger.LogDebug("Recherche des titres avec offset: {Offset}, limit: {Limit}", offset, limit); - this.logger.LogDebug("Préparation de la requête avec les inclusions Artiste et Styles"); var titres = this.context.Titres .OrderByDescending(t => t.DateCreation) @@ -125,12 +121,10 @@ public class DbTitreRepository : ITitreRepository try { this.logger.LogInformation("Incrémentation du nombre de lectures pour le titre ID: {IdTitre}", titre.IdTitre); - this.logger.LogDebug("Recherche du titre en base de données"); var existingTitre = this.context.Titres.Find(titre.IdTitre); if (existingTitre != null) { - this.logger.LogDebug("Titre trouvé, incrémentation du compteur de lectures"); existingTitre.NbLectures++; this.context.SaveChanges(); this.logger.LogDebug("Nouveau nombre de lectures: {NbLectures}", existingTitre.NbLectures); @@ -158,12 +152,10 @@ public class DbTitreRepository : ITitreRepository try { this.logger.LogInformation("Incrémentation du nombre de likes pour le titre ID: {IdTitre}", titre.IdTitre); - this.logger.LogDebug("Recherche du titre en base de données"); var existingTitre = this.context.Titres.Find(titre.IdTitre); if (existingTitre != null) { - this.logger.LogDebug("Titre trouvé, incrémentation du compteur de likes"); existingTitre.NbLikes++; this.context.SaveChanges(); this.logger.LogDebug("Nouveau nombre de likes: {NbLikes}", existingTitre.NbLikes); @@ -281,9 +273,6 @@ public class DbTitreRepository : ITitreRepository { try { - this.logger.LogDebug("Récupération de tous les titres"); - this.logger.LogDebug("Préparation de la requête avec les inclusions Artiste et Styles"); - var titres = this.context.Titres .Include(t => t.Artiste) .Include(t => t.Styles) @@ -307,7 +296,6 @@ public class DbTitreRepository : ITitreRepository try { this.logger.LogInformation("Recherche des titres par style: {Libelle}", libelle); - this.logger.LogDebug("Préparation de la requête de recherche par style"); var titres = this.context.Titres .Include(t => t.Artiste) @@ -325,4 +313,100 @@ public class DbTitreRepository : ITitreRepository throw; } } + + /// + public int CountLike() + { + try + { + var likes = this.context.Titres.Sum(t => t.NbLikes); + return likes; + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors de la récupération des likes."); + throw; + } + } + + /// + public int CountLecture() + { + try + { + var lectures = this.context.Titres.Sum(t => t.NbLectures); + return lectures; + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors de la récupération des lectures."); + throw; + } + } + + /// + public string? FindMostReviewedArtistName() + { + try + { + return this.context.Titres + .AsNoTracking() + .GroupBy(t => new { t.IdArtiste, t.Artiste.Nom }) + .OrderByDescending(g => g.Count()) + .ThenBy(g => g.Key.Nom) + .Select(g => g.Key.Nom) + .FirstOrDefault(); + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors de la recherche de l'artiste le plus chroniqué."); + throw; + } + } + + /// + public string? FindArtistNameWithMostReviewedAlbums() + { + try + { + return this.context.Titres + .AsNoTracking() + .GroupBy(t => new { t.IdArtiste, t.Artiste.Nom }) + .Select(g => new + { + g.Key.Nom, + AlbumCount = g.Select(t => t.Album).Distinct().Count(), + }) + .OrderByDescending(x => x.AlbumCount) + .ThenBy(x => x.Nom) + .Select(x => x.Nom) + .FirstOrDefault(); + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors de la recherche de l'artiste avec le plus d'albums chroniqués."); + throw; + } + } + + /// + public (int IdTitre, string Libelle)? FindMostPlayedTitle() + { + try + { + var result = this.context.Titres + .AsNoTracking() + .OrderByDescending(t => t.NbLectures) + .ThenBy(t => t.Libelle) + .Select(t => new { t.IdTitre, t.Libelle }) + .FirstOrDefault(); + + return result == null ? null : (result.IdTitre, result.Libelle); + } + catch (Exception ex) + { + this.logger.LogError(ex, "Erreur lors de la recherche du titre le plus joué."); + throw; + } + } } \ No newline at end of file diff --git a/Webzine.Repository/LocalArtisteRepository.cs b/Webzine.Repository/LocalArtisteRepository.cs index a1e95b9..754cb8e 100644 --- a/Webzine.Repository/LocalArtisteRepository.cs +++ b/Webzine.Repository/LocalArtisteRepository.cs @@ -107,6 +107,12 @@ namespace Webzine.Repository return this.dataStore.Artistes.Count(predicate); } + /// + public int CountWithBiography() + { + return this.dataStore.Artistes.Count(a => !string.IsNullOrEmpty(a.Biographie)); + } + /// public IEnumerable FindArtistes(int offset, int limit) { diff --git a/Webzine.Repository/LocalTitreRepository.cs b/Webzine.Repository/LocalTitreRepository.cs index 605a5c2..13e995e 100644 --- a/Webzine.Repository/LocalTitreRepository.cs +++ b/Webzine.Repository/LocalTitreRepository.cs @@ -131,4 +131,54 @@ public class LocalTitreRepository : ITitreRepository existingTitre.IdArtiste = titre.IdArtiste; existingTitre.Styles = titre.Styles; } + + /// + public int CountLike() + { + return this.dataStore.Titres.Sum(t => t.NbLikes); + } + + /// + public int CountLecture() + { + return this.dataStore.Titres.Sum(t => t.NbLectures); + } + + /// + public string? FindMostReviewedArtistName() + { + return this.dataStore.Titres + .GroupBy(t => t.Artiste) + .OrderByDescending(g => g.Count()) + .ThenBy(g => g.Key?.Nom) + .Select(g => g.Key?.Nom) + .FirstOrDefault(); + } + + /// + public string? FindArtistNameWithMostReviewedAlbums() + { + return this.dataStore.Titres + .GroupBy(t => t.Artiste) + .Select(g => new + { + ArtistName = g.Key?.Nom, + AlbumCount = g.Select(t => t.Album).Distinct().Count(), + }) + .OrderByDescending(x => x.AlbumCount) + .ThenBy(x => x.ArtistName) + .Select(x => x.ArtistName) + .FirstOrDefault(); + } + + /// + public (int IdTitre, string Libelle)? FindMostPlayedTitle() + { + Titre? titre = this.dataStore.Titres + .OrderByDescending(t => t.NbLectures) + .ThenBy(t => t.Libelle) + .FirstOrDefault(); + + return titre == null ? null : (titre.IdTitre, titre.Libelle); + } } \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs index c5df960..fd3d7f2 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs @@ -81,8 +81,8 @@ public class ArtisteController : Controller Biographie = model.Biographie, }; - // Persister les données. this.artisteRepository.Add(artiste); + this.logger.LogInformation("Création d'un nouvel artiste: {Nom}", artiste.Nom); // Renvoyer sur la page Index. return this.RedirectToAction("Index"); diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs index a42bdb4..0f19327 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs @@ -252,24 +252,18 @@ public class TitreController : Controller /// /// Méthode POST pour supprimer un titre. /// - /// Le titre à supprimer. + /// L'identifiant du titre à supprimer, utilisé pour récupérer les données du titre à partir de la liste des titres générés. /// Redirige vers la page d'index d'admin titre. [HttpPost] - public IActionResult Delete(AdminTitreDelete model) + public IActionResult DeleteTitre(int id) { - var titre = this.titreRepository.Find(model.Id); - - if (!this.ModelState.IsValid) - { - return this.View(model); - } + var titre = this.titreRepository.Find(id); if (titre != null) { this.titreRepository.Delete(titre); - return this.RedirectToAction("Index"); } - return this.View(model); + return this.RedirectToAction("Index"); } } \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Views/Titre/Delete.cshtml b/Webzine.WebApplication/Areas/Administration/Views/Titre/Delete.cshtml index 63a22e3..86f123b 100644 --- a/Webzine.WebApplication/Areas/Administration/Views/Titre/Delete.cshtml +++ b/Webzine.WebApplication/Areas/Administration/Views/Titre/Delete.cshtml @@ -1,5 +1,9 @@ @model Webzine.WebApplication.Areas.Administration.ViewModels.Titre.AdminTitreDelete +@{ + ViewData["Title"] = "Supprimer un titre"; +} +

Supprimer un titre

@@ -13,7 +17,7 @@ @Model.Artiste ?

-
+ diff --git a/Webzine.WebApplication/Controllers/TitreController.cs b/Webzine.WebApplication/Controllers/TitreController.cs index 3088dc5..8f00a16 100644 --- a/Webzine.WebApplication/Controllers/TitreController.cs +++ b/Webzine.WebApplication/Controllers/TitreController.cs @@ -101,25 +101,18 @@ namespace Webzine.WebApplication.Controllers /// /// Ajoute un like a un titre. /// - /// Modele contenant l'identifiant du titre. + /// Identifiant du titre a liker. /// Redirection vers la page detail. [HttpPost] - public IActionResult Like(TitreLike model) + public IActionResult Like(int id) { - this.logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre); - - var titre = this.titreRepository.Find(model.IdTitre); - - if (titre == null) - { - this.logger.LogWarning("Impossible d'ajouter un like. Titre ID {Id} introuvable.", model.IdTitre); - } - else + var titre = this.titreRepository.Find(id); + if (titre != null) { this.titreRepository.IncrementNbLikes(titre); } - return this.RedirectToAction("Index", new { id = model.IdTitre }); + return this.RedirectToAction("Index", new { id }); } /// @@ -130,29 +123,29 @@ namespace Webzine.WebApplication.Controllers [HttpPost] public IActionResult Comment(TitreComment model) { - var titre = this.titreRepository.Find(model.IdTitre); - - if (titre == null) + var titreToUpdate = this.titreRepository.Find(model.IdTitre); + if (titreToUpdate != null) { - this.logger.LogWarning("Impossible d'ajouter le commentaire. Titre ID {Id} introuvable.", model.IdTitre); - return this.RedirectToAction("Index"); + var commentaire = new Commentaire + { + Auteur = model.Auteur, + Contenu = model.Contenu, + DateCreation = DateTime.Now, + IdTitre = model.IdTitre, + }; + + titreToUpdate.Commentaires.Add(commentaire); + this.titreRepository.Update(titreToUpdate); } - var commentaire = new Commentaire - { - Auteur = model.Auteur, - Contenu = model.Contenu, - DateCreation = DateTime.Now, - IdTitre = model.IdTitre, - }; - - titre.Commentaires.Add(commentaire); - - this.logger.LogInformation("Commentaire ajoute avec succes au titre ID {Id}.", model.IdTitre); - return this.RedirectToAction("Index", new { id = model.IdTitre }); } + /// + /// Mappe une entite Titre vers un item de la liste de titres pour l'affichage dans la vue de style. + /// + /// Le titre à mapper. + /// L'item de la liste de titres. private static TitreStyleItem MapTitreItem(Titre titre) { return new TitreStyleItem @@ -166,10 +159,10 @@ namespace Webzine.WebApplication.Controllers } /// - /// + /// Construit une URL d'intégration Spotify à partir de l'URL d'écoute d'un titre. /// - /// - /// + /// L'URL d'écoute du titre. + /// L'URL d'intégration Spotify ou null si l'URL n'est pas valide. private static string? BuildSpotifyEmbedUrl(string? urlEcoute) { if (string.IsNullOrWhiteSpace(urlEcoute)) diff --git a/Webzine.WebApplication/Extensions/RouteConfiguration.cs b/Webzine.WebApplication/Extensions/RouteConfiguration.cs index 0b9b428..c499adb 100644 --- a/Webzine.WebApplication/Extensions/RouteConfiguration.cs +++ b/Webzine.WebApplication/Extensions/RouteConfiguration.cs @@ -23,6 +23,16 @@ public static class RouteConfiguration pattern: "artiste/{nom}", defaults: new { controller = "Artiste", action = "Index" }); + endpoints.MapControllerRoute( + name: "TitreLike", + pattern: "titre/{id}/like", + defaults: new { controller = "Titre", action = "Like" }); + + endpoints.MapControllerRoute( + name: "TitreComment", + pattern: "titre/{id}/comment", + defaults: new { controller = "Titre", action = "Comment" }); + // ----------- ADMIN ----------- var adminRoutes = new Dictionary { @@ -37,7 +47,7 @@ public static class RouteConfiguration defaults: new { area = "Administration", controller = route.Value, action = "Index" }); } - // --- AUTRE PROUTES --- + // --- AUTRES ROUTES --- endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); diff --git a/Webzine.WebApplication/Program.cs b/Webzine.WebApplication/Program.cs index 4ef5711..4490aad 100644 --- a/Webzine.WebApplication/Program.cs +++ b/Webzine.WebApplication/Program.cs @@ -127,7 +127,7 @@ try if (seederType == SeederType.Local) { - repo.SeedBaseDeDonnees(); + repo.SeedBaseDeDonnees(nbArtistes: 1000, nbTitres: 50000, maxStyles: 50); } else if (seederType == SeederType.Spotify) { @@ -150,11 +150,13 @@ try var commentaires = new List(); var titres = SeedDataLocal.GenererListeTitre(500, artistes, styles, albums); + int commentaireIdStart = 1; foreach (var titre in titres) { - var commentairesForTitre = SeedDataLocal.GenererListeCommentaire(titre, 0, 5); + var commentairesForTitre = SeedDataLocal.GenererListeCommentaire(titre, 0, 5, commentaireIdStart); titre.Commentaires.AddRange(commentairesForTitre); commentaires.AddRange(commentairesForTitre); + commentaireIdStart += commentairesForTitre.Count; } store.Artistes.AddRange(artistes); diff --git a/Webzine.WebApplication/Views/Titre/Index.cshtml b/Webzine.WebApplication/Views/Titre/Index.cshtml index 6b410bf..c5a1c6f 100644 --- a/Webzine.WebApplication/Views/Titre/Index.cshtml +++ b/Webzine.WebApplication/Views/Titre/Index.cshtml @@ -59,12 +59,11 @@