Merge pull request 'features/training-svc' (#3) from features/training-svc into main
Reviewed-on: #3pull/4/head^2
commit
6711aae0c0
@ -0,0 +1,10 @@
|
||||
namespace Shared.Enum;
|
||||
|
||||
public enum EDifficulty
|
||||
{
|
||||
None,
|
||||
Beginner,
|
||||
Intermediate,
|
||||
Advanced,
|
||||
Expert
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
namespace Shared.Enum;
|
||||
|
||||
public enum EGoal
|
||||
{
|
||||
None,
|
||||
MuscleGain,
|
||||
WeightLoss,
|
||||
Endurance,
|
||||
Strength,
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using System.Linq.Expressions;
|
||||
using Shared.Entities;
|
||||
|
||||
namespace Shared.Infrastructure;
|
||||
|
||||
public interface IRepository<T> where T : EntityBase
|
||||
{
|
||||
IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includes);
|
||||
|
||||
Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>>? expression = null, CancellationToken cancellationToken = default, params Expression<Func<T, object>>[] includes);
|
||||
|
||||
Task<T?> GetByIdAsync(object id, params Expression<Func<T, object>>[] includes);
|
||||
|
||||
Task InsertAsync(T obj);
|
||||
|
||||
void Update(T obj);
|
||||
|
||||
void Delete(object id);
|
||||
|
||||
//Task<PaginatedResult<T>> GetPaginatedListAsync(int pageNumber, int pageSize, string[]? orderBy = null, Expression<Func<T, bool>>? expression = null, CancellationToken cancellationToken = default, params Expression<Func<T, object>>[] includes);
|
||||
|
||||
Task<int> CountAsync(Expression<Func<T, bool>>? expression = null, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<bool> ExistsAsync(Expression<Func<T, bool>> expression, CancellationToken cancellationToken = default);
|
||||
|
||||
Task SaveChangesAsync();
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.IServices;
|
||||
|
||||
namespace TrainingSvc.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/training/[controller]")]
|
||||
public class ExercicesController : ControllerBase
|
||||
{
|
||||
private readonly IExerciceService _exerciceService;
|
||||
|
||||
public ExercicesController(IExerciceService exerciceService)
|
||||
{
|
||||
_exerciceService = exerciceService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<ExerciceDto>>> GetAll()
|
||||
{
|
||||
var list = await _exerciceService.GetAllAsync();
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<ExerciceDto>> GetById(string id)
|
||||
{
|
||||
var dto = await _exerciceService.GetByIdAsync(id);
|
||||
if (dto == null) return NotFound();
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<ExerciceDto>> Create([FromBody] CreateExerciceInstanceDto dto)
|
||||
{
|
||||
var created = await _exerciceService.CreateAsync(dto);
|
||||
return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(string id, [FromBody] UpdateExerciceInstanceDto dto)
|
||||
{
|
||||
var success = await _exerciceService.UpdateAsync(id, dto);
|
||||
if (!success) return NotFound();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
var success = await _exerciceService.DeleteAsync(id);
|
||||
if (!success) return NotFound();
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.IServices;
|
||||
|
||||
namespace TrainingSvc.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/training/[controller]")]
|
||||
public class SessionsController : ControllerBase
|
||||
{
|
||||
private readonly ISessionService _sessionService;
|
||||
|
||||
public SessionsController(ISessionService sessionService)
|
||||
{
|
||||
_sessionService = sessionService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<SessionDto>>> GetAll()
|
||||
{
|
||||
var list = await _sessionService.GetAllAsync();
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<SessionDto>> GetById(string id)
|
||||
{
|
||||
var dto = await _sessionService.GetByIdAsync(id);
|
||||
if (dto == null) return NotFound();
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<SessionDto>> Create([FromBody] CreateSessionDto dto)
|
||||
{
|
||||
var created = await _sessionService.CreateAsync(dto);
|
||||
return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(string id, [FromBody] UpdateSessionDto dto)
|
||||
{
|
||||
var success = await _sessionService.UpdateAsync(id, dto);
|
||||
if (!success) return NotFound();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
var success = await _sessionService.DeleteAsync(id);
|
||||
if (!success) return NotFound();
|
||||
return NoContent();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.IServices;
|
||||
|
||||
namespace TrainingSvc.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/training/[controller]")]
|
||||
public class TrainingProgramsController : ControllerBase
|
||||
{
|
||||
private readonly ITrainingProgramService _service;
|
||||
|
||||
public TrainingProgramsController(ITrainingProgramService service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<TrainingProgramDto>>> GetAll()
|
||||
{
|
||||
var list = await _service.GetAllAsync();
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<TrainingProgramDto>> GetById(string id)
|
||||
{
|
||||
var dto = await _service.GetByIdAsync(id);
|
||||
if (dto == null) return NotFound();
|
||||
return Ok(dto);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class CreateExerciceInstanceDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? ExerciceTemplateId { get; set; }
|
||||
public float Duration { get; set; }
|
||||
public int NbSets { get; set; }
|
||||
public int NbReps { get; set; }
|
||||
public float RestingTime { get; set; }
|
||||
public float? Weight { get; set; }
|
||||
public string? SessionId { get; set; }
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using Shared.Enum;
|
||||
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class CreateSessionDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int Day { get; set; }
|
||||
public ETarget? Target { get; set; }
|
||||
public string TrainingProgramId { get; set; }
|
||||
public List<CreateExerciceInstanceDto> Exercices { get; set; } = new();
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class ExerciceDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? ExerciceTemplateId { get; set; }
|
||||
public float Duration { get; set; }
|
||||
public int NbSets { get; set; }
|
||||
public int NbReps { get; set; }
|
||||
public float RestingTime { get; set; }
|
||||
public float? Weight { get; set; }
|
||||
public bool IsDone { get; set; }
|
||||
public string SessionId { get; set; }
|
||||
|
||||
// Champs du template (préfixés)
|
||||
public string? Name_Template { get; set; }
|
||||
public string? Description_Template { get; set; }
|
||||
public string? ImageUrl_Template { get; set; }
|
||||
public string? VideoUrl_Template { get; set; }
|
||||
public string? Target_Template { get; set; }
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using Shared.Enum;
|
||||
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class SessionDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int Day { get; set; }
|
||||
public ETarget? Target { get; set; }
|
||||
public string TrainingProgramId { get; set; }
|
||||
public List<ExerciceDto> Exercices { get; set; } = new();
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using Shared.Enum;
|
||||
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class TrainingProgramDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Lang { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int WeekDuration { get; set; }
|
||||
public int NbDays { get; set; }
|
||||
public string OwnerId { get; set; }
|
||||
public EGoal Goal { get; set; }
|
||||
public EDifficulty Difficulty { get; set; }
|
||||
public List<SessionDto> Sessions { get; set; } = new();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class UpdateExerciceInstanceDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public float Duration { get; set; }
|
||||
public int NbSets { get; set; }
|
||||
public int NbReps { get; set; }
|
||||
public float RestingTime { get; set; }
|
||||
public float? Weight { get; set; }
|
||||
public bool IsDone { get; set; }
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using Shared.Enum;
|
||||
|
||||
namespace TrainingSvc.DTOs;
|
||||
|
||||
public class UpdateSessionDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int Day { get; set; }
|
||||
public ETarget? Target { get; set; }
|
||||
public string TrainingProgramId { get; set; }
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using CatalogService.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Data;
|
||||
|
||||
public class TrainingDbContext : DbContext
|
||||
{
|
||||
public TrainingDbContext(DbContextOptions options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<ExerciceTemplate> ExerciceTemplates { get; set; }
|
||||
|
||||
public DbSet<ExerciceInstance> ExerciceInstances { get; set; }
|
||||
|
||||
public DbSet<Session> Sessions { get; set; }
|
||||
|
||||
public DbSet<TrainingProgram> TrainingPrograms { get; set; }
|
||||
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
using CatalogService.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Data;
|
||||
|
||||
public class TrainingDbInitializer
|
||||
{
|
||||
public static void InitDb(WebApplication app)
|
||||
{
|
||||
using var scope = app.Services.CreateScope();
|
||||
SeedData(scope.ServiceProvider.GetService<TrainingDbContext>());
|
||||
}
|
||||
|
||||
private static void SeedData(TrainingDbContext context)
|
||||
{
|
||||
context.Database.Migrate();
|
||||
|
||||
// 1. Seed ExerciceTemplates si vide
|
||||
if (!context.ExerciceTemplates.Any())
|
||||
{
|
||||
var exercices = new List<ExerciceTemplate>()
|
||||
{
|
||||
new ExerciceTemplate
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Squat",
|
||||
Description = "Squat is a compound exercise that targets the lower body, primarily the quadriceps, hamstrings, and glutes.",
|
||||
Target = Shared.Enum.ETarget.Legs,
|
||||
ImageUrl = "images/squat.jpg",
|
||||
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
|
||||
},
|
||||
new ExerciceTemplate
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Bench Press",
|
||||
Description = "Bench Press is a compound exercise that primarily targets the chest, shoulders, and triceps.",
|
||||
Target = Shared.Enum.ETarget.Chest,
|
||||
ImageUrl = "images/bench_press.jpg",
|
||||
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
|
||||
},
|
||||
new ExerciceTemplate
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Deadlift",
|
||||
Description = "Deadlift is a compound exercise that primarily targets the back, glutes, and hamstrings.",
|
||||
Target = Shared.Enum.ETarget.Back,
|
||||
ImageUrl = "images/deadlift.jpg",
|
||||
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
|
||||
},
|
||||
new ExerciceTemplate
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Shoulder Press",
|
||||
Description = "Shoulder Press is a compound exercise that primarily targets the shoulders and triceps.",
|
||||
Target = Shared.Enum.ETarget.Arms,
|
||||
ImageUrl = "images/shoulder_press.jpg",
|
||||
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
|
||||
},
|
||||
new ExerciceTemplate
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Running on Treadmill",
|
||||
Description = "Running on Treadmill is a cardiovascular exercise that primarily targets the legs and improves overall fitness.",
|
||||
Target = Shared.Enum.ETarget.Cardio,
|
||||
ImageUrl = "images/running_treadmill.jpg",
|
||||
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
|
||||
},
|
||||
};
|
||||
context.AddRange(exercices);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
// 2. Créer un TrainingProgram si aucun n'existe
|
||||
var program = context.TrainingPrograms.FirstOrDefault();
|
||||
if (program == null)
|
||||
{
|
||||
program = new TrainingProgram
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Programme de test"
|
||||
};
|
||||
context.TrainingPrograms.Add(program);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
// 3. Créer une session de test liée au programme si aucune n'existe
|
||||
var session = context.Sessions.FirstOrDefault();
|
||||
if (session == null)
|
||||
{
|
||||
session = new Session
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = "Session de test",
|
||||
TrainingProgramId = program.Id
|
||||
};
|
||||
context.Sessions.Add(session);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
// 4. Seed ExerciceInstances si vide
|
||||
if (!context.ExerciceInstances.Any() && context.ExerciceTemplates.Any())
|
||||
{
|
||||
var templates = context.ExerciceTemplates.ToList();
|
||||
|
||||
var instances = new List<ExerciceInstance>
|
||||
{
|
||||
new ExerciceInstance
|
||||
{
|
||||
Name = "Squat Série 1",
|
||||
ExerciceTemplateId = templates.First().Id,
|
||||
Duration = 60,
|
||||
NbSets = 4,
|
||||
NbReps = 10,
|
||||
RestingTime = 90,
|
||||
Weight = 80,
|
||||
IsDone = false,
|
||||
SessionId = session.Id
|
||||
},
|
||||
new ExerciceInstance
|
||||
{
|
||||
Name = "Bench Press Série 1",
|
||||
ExerciceTemplateId = templates.Skip(1).First().Id,
|
||||
Duration = 45,
|
||||
NbSets = 3,
|
||||
NbReps = 12,
|
||||
RestingTime = 60,
|
||||
Weight = 70,
|
||||
IsDone = false,
|
||||
SessionId = session.Id
|
||||
}
|
||||
};
|
||||
|
||||
context.ExerciceInstances.AddRange(instances);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using CatalogService.Entities;
|
||||
using Shared.Entities;
|
||||
|
||||
namespace TrainingSvc.Entities;
|
||||
|
||||
public class ExerciceInstance : EntityBase
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
public string? ExerciceTemplateId { get; set; }
|
||||
|
||||
public ExerciceTemplate? ExerciceTemplate { get; set; }
|
||||
|
||||
public float Duration { get; set; }
|
||||
|
||||
public int NbSets { get; set; }
|
||||
|
||||
public int NbReps { get; set; }
|
||||
|
||||
public float RestingTime { get; set; }
|
||||
|
||||
public float? Weight { get; set; }
|
||||
|
||||
public bool IsDone { get; set; }
|
||||
|
||||
[Required]
|
||||
public string SessionId { get; set; }
|
||||
|
||||
public Session Session { get; set; }
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Shared.Entities;
|
||||
using Shared.Enum;
|
||||
|
||||
namespace TrainingSvc.Entities;
|
||||
|
||||
public class Session : EntityBase
|
||||
{
|
||||
[Required]
|
||||
public required string Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
[Range(1, 7)]
|
||||
public int Day { get; set; }
|
||||
|
||||
public ETarget? Target { get; set; }
|
||||
|
||||
[Required]
|
||||
public string TrainingProgramId { get; set; }
|
||||
public TrainingProgram TrainingProgram { get; set; }
|
||||
|
||||
public virtual ICollection<ExerciceInstance> Exercices { get; set; } = new Collection<ExerciceInstance>();
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Shared.Entities;
|
||||
using Shared.Enum;
|
||||
|
||||
namespace TrainingSvc.Entities;
|
||||
|
||||
public class TrainingProgram : EntityBase
|
||||
{
|
||||
public string Lang { get; set; } = "en";
|
||||
|
||||
public required string Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public int WeekDuration { get; set; } = 4;
|
||||
|
||||
public int NbDays { get; set; } = 3;
|
||||
|
||||
public string OwnerId { get; set; } = "";
|
||||
|
||||
public EGoal Goal { get; set; } = EGoal.MuscleGain;
|
||||
|
||||
public EDifficulty Difficulty { get; set; } = EDifficulty.Beginner;
|
||||
|
||||
public virtual ICollection<Session> Sessions { get; set; } = new Collection<Session>();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using TrainingSvc.DTOs;
|
||||
|
||||
namespace TrainingSvc.IServices;
|
||||
|
||||
public interface IExerciceService
|
||||
{
|
||||
Task<IEnumerable<ExerciceDto>> GetAllAsync();
|
||||
Task<ExerciceDto?> GetByIdAsync(string id);
|
||||
Task<ExerciceDto> CreateAsync(CreateExerciceInstanceDto dto);
|
||||
Task<bool> UpdateAsync(string id, UpdateExerciceInstanceDto dto);
|
||||
Task<bool> DeleteAsync(string id);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using TrainingSvc.DTOs;
|
||||
|
||||
namespace TrainingSvc.IServices;
|
||||
|
||||
public interface ISessionService
|
||||
{
|
||||
Task<IEnumerable<SessionDto>> GetAllAsync();
|
||||
Task<SessionDto?> GetByIdAsync(string id);
|
||||
Task<SessionDto> CreateAsync(CreateSessionDto dto);
|
||||
Task<bool> UpdateAsync(string id, UpdateSessionDto dto);
|
||||
Task<bool> DeleteAsync(string id);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using TrainingSvc.DTOs;
|
||||
|
||||
namespace TrainingSvc.IServices;
|
||||
|
||||
public interface ITrainingProgramService
|
||||
{
|
||||
Task<IEnumerable<TrainingProgramDto>> GetAllAsync();
|
||||
Task<TrainingProgramDto?> GetByIdAsync(string id);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
using TrainingSvc.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrainingSvc.IServices;
|
||||
using TrainingSvc.Repositories;
|
||||
using TrainingSvc.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Pour chaque repository
|
||||
builder.Services.AddScoped<IExerciceTemplateRepository, ExerciceTemplateRepository>();
|
||||
builder.Services.AddScoped<IExerciceInstanceRepository, ExerciceInstanceRepository>();
|
||||
builder.Services.AddScoped<IExerciceService, ExerciceService>();
|
||||
builder.Services.AddScoped<ISessionRepository, SessionRepository>();
|
||||
builder.Services.AddScoped<ISessionService, SessionService>();
|
||||
builder.Services.AddScoped<ITrainingProgramRepository, TrainingProgramRepository>();
|
||||
builder.Services.AddScoped<ITrainingProgramService, TrainingProgramService>();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllers()
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter(System.Text.Json.JsonNamingPolicy.CamelCase, allowIntegerValues: false));
|
||||
});
|
||||
|
||||
builder.Services.AddDbContext<TrainingDbContext>(opt =>
|
||||
{
|
||||
opt.UseNpgsql(builder.Configuration.GetConnectionString("TrainingDb"));
|
||||
|
||||
});
|
||||
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
try
|
||||
{
|
||||
TrainingDbInitializer.InitDb(app);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
app.Run();
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
using TrainingSvc.Data;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public class ExerciceInstanceRepository : GenericRepository<ExerciceInstance>, IExerciceInstanceRepository
|
||||
{
|
||||
public ExerciceInstanceRepository(TrainingDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using CatalogService.Entities;
|
||||
using TrainingSvc.Data;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public class ExerciceTemplateRepository : GenericRepository<ExerciceTemplate>, IExerciceTemplateRepository
|
||||
{
|
||||
public ExerciceTemplateRepository(TrainingDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Shared.Entities;
|
||||
using Shared.Infrastructure;
|
||||
using TrainingSvc.Data;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
|
||||
public class GenericRepository<T> : IRepository<T> where T : EntityBase
|
||||
{
|
||||
protected readonly TrainingDbContext context;
|
||||
|
||||
public GenericRepository(TrainingDbContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includes)
|
||||
{
|
||||
IQueryable<T> query = this.context.Set<T>();
|
||||
|
||||
query = includes.Aggregate(query, (current, include) => current.Include(include));
|
||||
|
||||
return query.ToList<T>();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>>? expression = null, CancellationToken cancellationToken = default, params Expression<Func<T, object>>[] includes)
|
||||
{
|
||||
IQueryable<T> query = this.context.Set<T>();
|
||||
query = includes.Aggregate(query, (current, include) => current.Include(include));
|
||||
if (expression != null) query = query.Where(expression);
|
||||
|
||||
return await query.ToDynamicListAsync<T>(cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public virtual async Task<T?> GetByIdAsync(object id, params Expression<Func<T, object>>[] includes)
|
||||
{
|
||||
IQueryable<T> query = this.context.Set<T>();
|
||||
|
||||
query = query.Where(entity => entity.Id.Equals(id));
|
||||
|
||||
query = includes.Aggregate(query, (current, include) => current.Include(include));
|
||||
|
||||
return await query.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task InsertAsync(T obj)
|
||||
{
|
||||
_ = await this.context.Set<T>()
|
||||
.AddAsync(obj);
|
||||
}
|
||||
|
||||
public void Update(T obj)
|
||||
{
|
||||
_ = this.context.Set<T>().Attach(obj);
|
||||
this.context.Entry(obj).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Delete(object id)
|
||||
{
|
||||
var existing = this.context
|
||||
.Set<T>()
|
||||
.Find(id);
|
||||
|
||||
_ = this.context.Set<T>().Remove(existing!);
|
||||
}
|
||||
|
||||
/*public async Task<PaginatedResult<T>> GetPaginatedListAsync(
|
||||
int pageNumber,
|
||||
int pageSize,
|
||||
string[]? orderBy = null,
|
||||
Expression<Func<T, bool>>? expression = null,
|
||||
CancellationToken cancellationToken = default,
|
||||
params Expression<Func<T, object>>[] includes)
|
||||
{
|
||||
IQueryable<T> query = this.context.Set<T>();
|
||||
|
||||
query = includes.Aggregate(query, (current, include) => current.Include(include));
|
||||
|
||||
if (expression != null) query = query.Where(expression);
|
||||
|
||||
var ordering = orderBy?.Any() == true ? string.Join(",", orderBy) : null;
|
||||
|
||||
query = !string.IsNullOrWhiteSpace(ordering) ? query.OrderBy(ordering) : query.OrderBy(a => a.Id);
|
||||
|
||||
var count = await query
|
||||
.AsNoTracking()
|
||||
.CountAsync(cancellationToken: cancellationToken);
|
||||
|
||||
var items = await query
|
||||
.Skip(pageNumber * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToDynamicListAsync<T>(cancellationToken: cancellationToken);
|
||||
|
||||
return new PaginatedResult<T>(items, count, pageNumber, pageSize);
|
||||
}*/
|
||||
|
||||
public async Task<int> CountAsync(Expression<Func<T, bool>>? expression = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
IQueryable<T> query = this.context.Set<T>();
|
||||
if (expression != null) query = query.Where(expression);
|
||||
|
||||
return await query
|
||||
.AsNoTracking()
|
||||
.CountAsync(cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(Expression<Func<T, bool>> expression, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await this.context.Set<T>().AnyAsync(expression, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public async Task SaveChangesAsync()
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using CatalogService.Entities;
|
||||
using Shared.Infrastructure;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public interface IExerciceInstanceRepository : IRepository<ExerciceInstance>
|
||||
{
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
using CatalogService.Entities;
|
||||
using Shared.Infrastructure;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public interface IExerciceTemplateRepository : IRepository<ExerciceTemplate>
|
||||
{
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
using Shared.Infrastructure;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public interface ISessionRepository : IRepository<Session>
|
||||
{
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using Shared.Infrastructure;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public interface ITrainingProgramRepository : IRepository<TrainingProgram>
|
||||
{
|
||||
Task<IEnumerable<TrainingProgram>> GetAllWithSessionsAsync();
|
||||
Task<TrainingProgram?> GetByIdWithSessionsAsync(string id);
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrainingSvc.Data;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public class SessionRepository : GenericRepository<Session>, ISessionRepository
|
||||
{
|
||||
public SessionRepository(TrainingDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Session>> GetAllWithExercicesAndTemplatesAsync()
|
||||
{
|
||||
return await context.Sessions
|
||||
.Include(s => s.Exercices)
|
||||
.ThenInclude(e => e.ExerciceTemplate)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Session?> GetByIdWithExercicesAndTemplatesAsync(string id)
|
||||
{
|
||||
return await context.Sessions
|
||||
.Include(s => s.Exercices)
|
||||
.ThenInclude(e => e.ExerciceTemplate)
|
||||
.FirstOrDefaultAsync(s => s.Id == id);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrainingSvc.Data;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.Repositories;
|
||||
|
||||
public class TrainingProgramRepository : GenericRepository<TrainingProgram>, ITrainingProgramRepository
|
||||
{
|
||||
public TrainingProgramRepository(TrainingDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TrainingProgram>> GetAllWithSessionsAsync()
|
||||
{
|
||||
return await context.TrainingPrograms
|
||||
.Include(tp => tp.Sessions)
|
||||
.ThenInclude(s => s.Exercices)
|
||||
.ThenInclude(e => e.ExerciceTemplate)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<TrainingProgram?> GetByIdWithSessionsAsync(string id)
|
||||
{
|
||||
return await context.TrainingPrograms
|
||||
.Include(tp => tp.Sessions)
|
||||
.ThenInclude(s => s.Exercices)
|
||||
.ThenInclude(e => e.ExerciceTemplate)
|
||||
.FirstOrDefaultAsync(tp => tp.Id == id);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using AutoMapper;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.Entities;
|
||||
using CatalogService.Entities;
|
||||
|
||||
namespace TrainingSvc.RequestHelpers;
|
||||
|
||||
public class ExerciceInstanceProfile : Profile
|
||||
{
|
||||
public ExerciceInstanceProfile()
|
||||
{
|
||||
CreateMap<ExerciceInstance, ExerciceDto>()
|
||||
.ForMember(dest => dest.Name_Template, opt => opt.MapFrom(src => src.ExerciceTemplate != null ? src.ExerciceTemplate.Name : null))
|
||||
.ForMember(dest => dest.Description_Template, opt => opt.MapFrom(src => src.ExerciceTemplate != null ? src.ExerciceTemplate.Description : null))
|
||||
.ForMember(dest => dest.ImageUrl_Template, opt => opt.MapFrom(src => src.ExerciceTemplate != null ? src.ExerciceTemplate.ImageUrl : null))
|
||||
.ForMember(dest => dest.VideoUrl_Template, opt => opt.MapFrom(src => src.ExerciceTemplate != null ? src.ExerciceTemplate.VideoUrl : null))
|
||||
.ForMember(dest => dest.Target_Template, opt => opt.MapFrom(src => src.ExerciceTemplate != null ? src.ExerciceTemplate.Target.ToString() : null));
|
||||
|
||||
CreateMap<CreateExerciceInstanceDto, ExerciceInstance>();
|
||||
CreateMap<UpdateExerciceInstanceDto, ExerciceInstance>();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using AutoMapper;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.RequestHelpers;
|
||||
|
||||
public class SessionProfile : Profile
|
||||
{
|
||||
public SessionProfile()
|
||||
{
|
||||
CreateMap<Session, SessionDto>()
|
||||
.ForMember(dest => dest.Exercices, opt
|
||||
=> opt.MapFrom(src => src.Exercices));
|
||||
CreateMap<CreateSessionDto, Session>()
|
||||
.ForMember(dest => dest.Exercices, opt => opt.Ignore()); // Ignore Exercices to handle them separately
|
||||
CreateMap<UpdateSessionDto, Session>();
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using AutoMapper;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.Entities;
|
||||
|
||||
namespace TrainingSvc.RequestHelpers;
|
||||
|
||||
public class TrainingProgramProfile : Profile
|
||||
{
|
||||
public TrainingProgramProfile()
|
||||
{
|
||||
CreateMap<TrainingProgram, TrainingProgramDto>();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
using AutoMapper;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.Entities;
|
||||
using TrainingSvc.Repositories;
|
||||
using TrainingSvc.IServices;
|
||||
|
||||
namespace TrainingSvc.Services;
|
||||
|
||||
public class ExerciceService : IExerciceService
|
||||
{
|
||||
private readonly IExerciceInstanceRepository _exerciceRepo;
|
||||
private readonly IExerciceTemplateRepository _exerciceTemplateRepo;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public ExerciceService(
|
||||
IExerciceInstanceRepository exerciceRepo,
|
||||
IExerciceTemplateRepository exerciceTemplateRepo,
|
||||
IMapper mapper)
|
||||
{
|
||||
_exerciceRepo = exerciceRepo;
|
||||
_exerciceTemplateRepo = exerciceTemplateRepo;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ExerciceDto>> GetAllAsync()
|
||||
{
|
||||
var list = await _exerciceRepo.GetAllAsync(null, default, e => e.ExerciceTemplate);
|
||||
return _mapper.Map<IEnumerable<ExerciceDto>>(list);
|
||||
}
|
||||
|
||||
public async Task<ExerciceDto?> GetByIdAsync(string id)
|
||||
{
|
||||
var entity = await _exerciceRepo.GetByIdAsync(id, e => e.ExerciceTemplate);
|
||||
return entity == null ? null : _mapper.Map<ExerciceDto>(entity);
|
||||
}
|
||||
|
||||
public async Task<ExerciceDto> CreateAsync(CreateExerciceInstanceDto dto)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dto.ExerciceTemplateId))
|
||||
{
|
||||
var exists = await _exerciceTemplateRepo.ExistsAsync(t => t.Id == dto.ExerciceTemplateId);
|
||||
if (!exists)
|
||||
throw new ArgumentException("ExerciceTemplateId invalide.");
|
||||
}
|
||||
|
||||
var entity = _mapper.Map<ExerciceInstance>(dto);
|
||||
await _exerciceRepo.InsertAsync(entity);
|
||||
await _exerciceRepo.SaveChangesAsync(); // Persiste en base
|
||||
|
||||
return _mapper.Map<ExerciceDto>(entity);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(string id, UpdateExerciceInstanceDto dto)
|
||||
{
|
||||
var entity = await _exerciceRepo.GetByIdAsync(id, e => e.ExerciceTemplate);
|
||||
if (entity == null) return false;
|
||||
|
||||
_mapper.Map(dto, entity);
|
||||
_exerciceRepo.Update(entity);
|
||||
await _exerciceRepo.SaveChangesAsync(); // Persiste en base
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(string id)
|
||||
{
|
||||
var entity = await _exerciceRepo.GetByIdAsync(id);
|
||||
if (entity == null) return false;
|
||||
_exerciceRepo.Delete(id);
|
||||
await _exerciceRepo.SaveChangesAsync(); // Persiste en base
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
using AutoMapper;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.Entities;
|
||||
using TrainingSvc.Repositories;
|
||||
using TrainingSvc.IServices;
|
||||
|
||||
namespace TrainingSvc.Services;
|
||||
|
||||
public class SessionService : ISessionService
|
||||
{
|
||||
private readonly ISessionRepository _sessionRepo;
|
||||
private readonly IExerciceInstanceRepository _exerciceRepo;
|
||||
private readonly IExerciceTemplateRepository _exerciceTemplateRepo;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public SessionService(
|
||||
ISessionRepository sessionRepo,
|
||||
IExerciceInstanceRepository exerciceRepo,
|
||||
IExerciceTemplateRepository exerciceTemplateRepo,
|
||||
IMapper mapper)
|
||||
{
|
||||
_sessionRepo = sessionRepo;
|
||||
_exerciceRepo = exerciceRepo;
|
||||
_exerciceTemplateRepo = exerciceTemplateRepo;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SessionDto>> GetAllAsync()
|
||||
{
|
||||
var sessions = await ((SessionRepository)_sessionRepo).GetAllWithExercicesAndTemplatesAsync();
|
||||
return _mapper.Map<IEnumerable<SessionDto>>(sessions);
|
||||
}
|
||||
|
||||
public async Task<SessionDto?> GetByIdAsync(string id)
|
||||
{
|
||||
var entity = await ((SessionRepository)_sessionRepo).GetByIdWithExercicesAndTemplatesAsync(id);
|
||||
return entity == null ? null : _mapper.Map<SessionDto>(entity);
|
||||
}
|
||||
|
||||
public async Task<SessionDto> CreateAsync(CreateSessionDto dto)
|
||||
{
|
||||
// Ne mappe pas les exercices ici
|
||||
var session = _mapper.Map<Session>(dto);
|
||||
session.Exercices.Clear(); // S'assure qu'il n'y a pas d'exercices liés
|
||||
|
||||
await _sessionRepo.InsertAsync(session);
|
||||
await _sessionRepo.SaveChangesAsync();
|
||||
|
||||
foreach (var exoDto in dto.Exercices)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(exoDto.ExerciceTemplateId))
|
||||
{
|
||||
var exists = await _exerciceTemplateRepo.ExistsAsync(t => t.Id == exoDto.ExerciceTemplateId);
|
||||
if (!exists)
|
||||
throw new ArgumentException("ExerciceTemplateId invalide.");
|
||||
}
|
||||
var exo = _mapper.Map<ExerciceInstance>(exoDto);
|
||||
exo.SessionId = session.Id;
|
||||
await _exerciceRepo.InsertAsync(exo);
|
||||
}
|
||||
await _exerciceRepo.SaveChangesAsync();
|
||||
|
||||
var entity = await ((SessionRepository)_sessionRepo).GetByIdWithExercicesAndTemplatesAsync(session.Id);
|
||||
return _mapper.Map<SessionDto>(entity);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(string id, UpdateSessionDto dto)
|
||||
{
|
||||
var session = await ((SessionRepository)_sessionRepo).GetByIdWithExercicesAndTemplatesAsync(id);
|
||||
if (session == null) return false;
|
||||
|
||||
_mapper.Map(dto, session);
|
||||
_sessionRepo.Update(session);
|
||||
await _sessionRepo.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(string id)
|
||||
{
|
||||
var session = await _sessionRepo.GetByIdAsync(id, s => s.Exercices);
|
||||
if (session == null) return false;
|
||||
|
||||
foreach (var ex in session.Exercices.ToList())
|
||||
{
|
||||
_exerciceRepo.Delete(ex.Id);
|
||||
}
|
||||
await _exerciceRepo.SaveChangesAsync();
|
||||
|
||||
_sessionRepo.Delete(id);
|
||||
await _sessionRepo.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using AutoMapper;
|
||||
using TrainingSvc.DTOs;
|
||||
using TrainingSvc.IServices;
|
||||
using TrainingSvc.Repositories;
|
||||
|
||||
namespace TrainingSvc.Services;
|
||||
|
||||
public class TrainingProgramService : ITrainingProgramService
|
||||
{
|
||||
private readonly ITrainingProgramRepository _repo;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public TrainingProgramService(ITrainingProgramRepository repo, IMapper mapper)
|
||||
{
|
||||
_repo = repo;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TrainingProgramDto>> GetAllAsync()
|
||||
{
|
||||
var list = await _repo.GetAllWithSessionsAsync();
|
||||
return _mapper.Map<IEnumerable<TrainingProgramDto>>(list);
|
||||
}
|
||||
|
||||
public async Task<TrainingProgramDto?> GetByIdAsync(string id)
|
||||
{
|
||||
var entity = await _repo.GetByIdWithSessionsAsync(id);
|
||||
return entity == null ? null : _mapper.Map<TrainingProgramDto>(entity);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,6 @@
|
||||
@TrainingSvc_HostAddress = http://localhost:5269
|
||||
|
||||
GET {{TrainingSvc_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
Loading…
Reference in new issue