From e47a50e0da0e96ac09f5a35124c0e2ea49426813 Mon Sep 17 00:00:00 2001
From: mirage <119869686+ClementBobin@users.noreply.github.com>
Date: Wed, 1 Apr 2026 13:12:00 +0200
Subject: [PATCH 1/3] =?UTF-8?q?feat:=20impl=C3=A9menter=20le=20service=20d?=
=?UTF-8?q?e=20tableau=20de=20bord=20et=20DTO=20pour=20les=20statistiques?=
=?UTF-8?q?=20du=20tableau=20de=20bord?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Dto/DashboardDTO.cs | 6 +-
.../IDashboardService.cs | 16 +++++
Webzine.Business/DashboardService.cs | 71 +++++++++++++++++++
Webzine.Business/Webzine.Business.csproj | 5 ++
.../IArtisteRepository.cs | 13 ++++
.../IStyleRepository.cs | 6 ++
Webzine.Repository/DbArtisteRepository.cs | 32 +++++++++
Webzine.Repository/DbStyleRepository.cs | 16 +++++
Webzine.Repository/LocalArtisteRepository.cs | 12 ++++
Webzine.Repository/LocalStyleRepository.cs | 6 ++
.../Controllers/DashboardController.cs | 54 +++-----------
.../Views/Dashboard/Index.cshtml | 3 +-
Webzine.WebApplication/Program.cs | 6 ++
.../Webzine.WebApplication.csproj | 2 +
14 files changed, 198 insertions(+), 50 deletions(-)
rename Webzine.WebApplication/Areas/Administration/ViewModels/DashboardViewModel.cs => Webzine.Business.Contracts/Dto/DashboardDTO.cs (90%)
create mode 100644 Webzine.Business.Contracts/IDashboardService.cs
create mode 100644 Webzine.Business/DashboardService.cs
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 @@
+
+
From 9818f785f9131f53417a2c73b9408091e5dceab3 Mon Sep 17 00:00:00 2001
From: mirage <119869686+ClementBobin@users.noreply.github.com>
Date: Wed, 1 Apr 2026 16:00:38 +0200
Subject: [PATCH 2/3] =?UTF-8?q?fix:=20optimiser=20la=20r=C3=A9cup=C3=A9rat?=
=?UTF-8?q?ion=20du=20nombre=20de=20styles=20en=20utilisant=20Enumerable.C?=
=?UTF-8?q?ount?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Webzine.Repository/DbStyleRepository.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Webzine.Repository/DbStyleRepository.cs b/Webzine.Repository/DbStyleRepository.cs
index 0a6894b..a189ffd 100644
--- a/Webzine.Repository/DbStyleRepository.cs
+++ b/Webzine.Repository/DbStyleRepository.cs
@@ -184,7 +184,7 @@ public class DbStyleRepository : IStyleRepository
{
try
{
- int count = this.context.Styles.Count();
+ int count = Enumerable.Count(this.context.Styles);
this.logger.LogDebug("Nombre total de styles: {Count}", count);
return count;
}
From 07d414fc8277c21257a70502991580876104b03d Mon Sep 17 00:00:00 2001
From: mirage <119869686+ClementBobin@users.noreply.github.com>
Date: Wed, 1 Apr 2026 16:22:53 +0200
Subject: [PATCH 3/3] =?UTF-8?q?fix:=20optimiser=20la=20r=C3=A9cup=C3=A9rat?=
=?UTF-8?q?ion=20du=20nombre=20total=20d'artistes=20en=20utilisant=20Enume?=
=?UTF-8?q?rable.Count?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Webzine.Repository/DbArtisteRepository.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Webzine.Repository/DbArtisteRepository.cs b/Webzine.Repository/DbArtisteRepository.cs
index 0b3748f..f6f868b 100644
--- a/Webzine.Repository/DbArtisteRepository.cs
+++ b/Webzine.Repository/DbArtisteRepository.cs
@@ -182,7 +182,7 @@ namespace Webzine.Repository
{
try
{
- int count = this.context.Artistes.Count();
+ int count = Enumerable.Count(this.context.Artistes);
this.logger.LogDebug("Nombre total d'artistes dans la base: {Count}", count);
return count;
}