diff --git a/Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs b/Webzine.Business.Contracts/Dto/DashboardDTO.cs similarity index 90% rename from Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs rename to Webzine.Business.Contracts/Dto/DashboardDTO.cs index 106779d..9509c6c 100644 --- a/Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs +++ b/Webzine.Business.Contracts/Dto/DashboardDTO.cs @@ -1,9 +1,9 @@ -namespace Webzine.WebApplication.Areas.Administration.ViewModels; +namespace Webzine.Business.Contracts.Dto; /// -/// ViewModel pour le tableau de bord de l'administration du webzine. +/// DTO pour le tableau de bord de l'administration du webzine. /// -public class DashboardViewModel +public class DashboardDTO { /// /// Définit le nombre total d'artistes chroniqués dans le webzine. diff --git a/Webzine.Business.Contracts/IDashboardService.cs b/Webzine.Business.Contracts/IDashboardService.cs new file mode 100644 index 0000000..8bde456 --- /dev/null +++ b/Webzine.Business.Contracts/IDashboardService.cs @@ -0,0 +1,16 @@ +using Webzine.Business.Contracts.Dto; + +namespace Webzine.Business.Contracts; + +/// +/// Service responsable du calcul des statistiques affichées sur le tableau de bord d'administration. +/// Agrège les données provenant de plusieurs repositories pour produire un résumé cohérent. +/// +public interface IDashboardService +{ + /// + /// Calcule et retourne toutes les statistiques du tableau de bord en une seule passe. + /// + /// Un contenant les agrégats calculés. + DashboardDTO GetDashboardData(); +} \ No newline at end of file diff --git a/Webzine.Business/DashboardService.cs b/Webzine.Business/DashboardService.cs new file mode 100644 index 0000000..d9e9812 --- /dev/null +++ b/Webzine.Business/DashboardService.cs @@ -0,0 +1,71 @@ +using Webzine.Business.Contracts; +using Webzine.Entity; +using Webzine.Repository.Contracts; + +namespace Webzine.Business; + +using Contracts.Dto; + +/// +/// Implémentation de . +/// Orchestre plusieurs appels aux repositories pour produire les statistiques du tableau de bord. +/// +public class DashboardService : IDashboardService +{ + private readonly IArtisteRepository artisteRepository; + private readonly ITitreRepository titreRepository; + private readonly IStyleRepository styleRepository; + + /// + /// Initializes a new instance of the class. + /// + /// Repository des artistes. + /// Repository des titres. + /// Repository des styles. + public DashboardService( + IArtisteRepository artisteRepository, + ITitreRepository titreRepository, + IStyleRepository styleRepository) + { + this.artisteRepository = artisteRepository; + this.titreRepository = titreRepository; + this.styleRepository = styleRepository; + } + + /// + 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(); + + 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, + NombreTitres = this.titreRepository.Count(), + NombreGenres = this.styleRepository.Count(), + NombreLectures = titres.Sum(t => t.NbLectures), + NombreLikes = titres.Sum(t => t.NbLikes), + }; + } +} \ No newline at end of file diff --git a/Webzine.Business/Webzine.Business.csproj b/Webzine.Business/Webzine.Business.csproj index 33c0658..6e5a6aa 100644 --- a/Webzine.Business/Webzine.Business.csproj +++ b/Webzine.Business/Webzine.Business.csproj @@ -21,4 +21,9 @@ + + + + + diff --git a/Webzine.Repository.Contracts/IArtisteRepository.cs b/Webzine.Repository.Contracts/IArtisteRepository.cs index bc13f76..ee170b5 100644 --- a/Webzine.Repository.Contracts/IArtisteRepository.cs +++ b/Webzine.Repository.Contracts/IArtisteRepository.cs @@ -51,5 +51,18 @@ namespace Webzine.Repository.Contracts /// Nom de l'artiste. /// IEnumarble. qui contient la chaine de caractere. IEnumerable Search(string nom); + + /// + /// Récupère le nombre total d'artistes dans la collection. + /// + /// Le nombre total d'artistes. + int Count(); + + /// + /// Récupère le nombre d'artistes correspondant au prédicat fourni. + /// + /// Le prédicat de filtrage. + /// Le nombre d'artistes correspondants. + int Count(Func predicate); } } \ No newline at end of file diff --git a/Webzine.Repository.Contracts/IStyleRepository.cs b/Webzine.Repository.Contracts/IStyleRepository.cs index d260039..0ea9ba3 100644 --- a/Webzine.Repository.Contracts/IStyleRepository.cs +++ b/Webzine.Repository.Contracts/IStyleRepository.cs @@ -37,5 +37,11 @@ namespace Webzine.Repository.Contracts /// /// L'objet style à mettre à jour. + /// Récupère le nombre total de styles dans la liste des styles. + /// + /// Le nombre total de styles présents dans la liste. + int Count(); } } \ No newline at end of file diff --git a/Webzine.Repository/DbArtisteRepository.cs b/Webzine.Repository/DbArtisteRepository.cs index c27c1d7..0b3748f 100644 --- a/Webzine.Repository/DbArtisteRepository.cs +++ b/Webzine.Repository/DbArtisteRepository.cs @@ -176,5 +176,37 @@ namespace Webzine.Repository throw new Exception("Erreur lors de la recherche d'artiste {error}", ex); } } + + /// + public int Count() + { + try + { + int count = this.context.Artistes.Count(); + 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; + } + } + + /// + public int Count(Func 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; + } + } } } \ No newline at end of file diff --git a/Webzine.Repository/DbStyleRepository.cs b/Webzine.Repository/DbStyleRepository.cs index c350a4f..0a6894b 100644 --- a/Webzine.Repository/DbStyleRepository.cs +++ b/Webzine.Repository/DbStyleRepository.cs @@ -178,4 +178,20 @@ public class DbStyleRepository : IStyleRepository throw; } } + + /// + public int Count() + { + try + { + int count = this.context.Styles.Count(); + 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; + } + } } \ No newline at end of file diff --git a/Webzine.Repository/LocalArtisteRepository.cs b/Webzine.Repository/LocalArtisteRepository.cs index 69021c5..0388da3 100644 --- a/Webzine.Repository/LocalArtisteRepository.cs +++ b/Webzine.Repository/LocalArtisteRepository.cs @@ -93,5 +93,17 @@ namespace Webzine.Repository .Where(a => a.Nom.ToLower().Contains(mot.ToLower())) .ToList(); } + + /// + public int Count() + { + return this.dataStore.Artistes.Count; + } + + /// + public int Count(Func predicate) + { + return this.dataStore.Artistes.Count(predicate); + } } } \ No newline at end of file diff --git a/Webzine.Repository/LocalStyleRepository.cs b/Webzine.Repository/LocalStyleRepository.cs index 0b4d16c..e750ff2 100644 --- a/Webzine.Repository/LocalStyleRepository.cs +++ b/Webzine.Repository/LocalStyleRepository.cs @@ -56,4 +56,10 @@ public class LocalStyleRepository : IStyleRepository { throw new NotSupportedException("Mode local"); } + + /// + public int Count() + { + return this.dataStore.Styles.Count; + } } \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs index 2f314ad..97f1721 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs @@ -1,30 +1,26 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers; +using Webzine.Business.Contracts; +using Webzine.Business.Contracts.Dto; using Microsoft.AspNetCore.Mvc; - using Webzine.Repository.Contracts; -using Webzine.WebApplication.Areas.Administration.ViewModels; [Area("Administration")] public class DashboardController : Controller { private readonly ILogger logger; - private readonly IStyleRepository styleRepository; - private readonly IArtisteRepository artisteRepository; - private readonly ITitreRepository titreRepository; + private readonly IDashboardService dashboardService; /// /// Initializes a new instance of the class. /// Initialise une nouvelle instance de la classe . /// /// Service de journalisation injecté. - /// Repository des styles injecté. - public DashboardController(ILogger logger, IStyleRepository styleRepository, IArtisteRepository artisteRepository, ITitreRepository titreRepository) + /// Service de calcul des statistiques du tableau de bord. + public DashboardController(ILogger logger, IDashboardService dashboardService) { this.logger = logger; - this.styleRepository = styleRepository; - this.artisteRepository = artisteRepository; - this.titreRepository = titreRepository; + this.dashboardService = dashboardService; this.logger.LogInformation("Initialisation du contrôleur TitreController."); } @@ -35,42 +31,8 @@ public class DashboardController : Controller /// La vue Index du tableau de bord. public IActionResult Index() { - var artisteLePlusChronique = this.titreRepository.FindAll() - .GroupBy(t => t.Artiste) - .OrderByDescending(g => g.Count()) - .First(); + DashboardDTO data = dashboardService.GetDashboardData(); - var albumLePlusChronique = this.titreRepository.FindAll() - .GroupBy(t => t.Artiste) - .OrderByDescending(g => g.Select(t => t.Album).Distinct().Count()) - .First(); - - var musiqueLaPlusJouee = this.titreRepository.FindAll() - .OrderByDescending(t => t.NbLectures) - .First(); - - var model = new DashboardViewModel - { - NombreArtistes = this.artisteRepository.FindAll().Count(), - - ArtisteLePlusChronique = artisteLePlusChronique.Key.Nom, - - AlbumLePlusChronique = albumLePlusChronique.Key.Nom, - - NombreBiographies = this.artisteRepository.FindAll().Count(a => !string.IsNullOrEmpty(a.Biographie)), - - IdMusiqueLaPlusJouee = musiqueLaPlusJouee.IdTitre, - MusiqueLaPlusJouee = musiqueLaPlusJouee.Libelle, - - NombreTitres = this.titreRepository.Count(), - - NombreGenres = this.styleRepository.FindAll().Count(), - - NombreLectures = this.titreRepository.FindAll().Sum(t => t.NbLectures), - - NombreLikes = this.titreRepository.FindAll().Sum(t => t.NbLikes), - }; - - return this.View(model); + return this.View(data); } } \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml b/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml index ac676f2..65217be 100644 --- a/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml +++ b/Webzine.WebApplication/Areas/Administration/Views/Dashboard/Index.cshtml @@ -1,4 +1,5 @@ -@model Webzine.WebApplication.Areas.Administration.ViewModels.DashboardViewModel +@using Webzine.Business.Contracts.Dto +@model DashboardDTO

Tableau de bord

diff --git a/Webzine.WebApplication/Program.cs b/Webzine.WebApplication/Program.cs index 916d86e..d118c67 100644 --- a/Webzine.WebApplication/Program.cs +++ b/Webzine.WebApplication/Program.cs @@ -6,6 +6,8 @@ using Microsoft.EntityFrameworkCore; using NLog; using NLog.Web; +using Webzine.Business; +using Webzine.Business.Contracts; using Webzine.EntitiesContext; using Webzine.Entity; using Webzine.Entity.Fixtures; @@ -67,6 +69,8 @@ try builder.Services.AddSingleton(); } + builder.Services.AddScoped(); + // https://learn.microsoft.com/fr-fr/aspnet/core/performance/response-compression?view=aspnetcore-10.0#configuration // Ajoute le service de compression des réponses HTTP pour réduire la taille des données envoyées au client et améliorer les performances de l'application. builder.Services.AddResponseCompression(); @@ -78,6 +82,8 @@ try using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + if (shouldSeed) { db.Database.EnsureDeleted(); diff --git a/Webzine.WebApplication/Webzine.WebApplication.csproj b/Webzine.WebApplication/Webzine.WebApplication.csproj index f47a272..5f8c0ca 100644 --- a/Webzine.WebApplication/Webzine.WebApplication.csproj +++ b/Webzine.WebApplication/Webzine.WebApplication.csproj @@ -34,6 +34,8 @@ + +