Merge pull request 'features/training-svc' (#3) from features/training-svc into main

Reviewed-on: #3
pull/4/head^2
Leo TUAILLON 2 days ago
commit 6711aae0c0

@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalogService", "src\Catal
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "src\Shared\Shared.csproj", "{BF49B348-4188-4AC7-9ED4-5837F4B3BCD2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainingSvc", "src\TrainingSvc\TrainingSvc.csproj", "{F16873D3-6511-48AA-B74E-8B13D5F20F20}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentitySvc", "src\IdentitySvc\IdentitySvc.csproj", "{74C8ACD5-5DC4-4466-8846-B552FF131304}"
EndProject
Global
@ -28,6 +30,10 @@ Global
{BF49B348-4188-4AC7-9ED4-5837F4B3BCD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF49B348-4188-4AC7-9ED4-5837F4B3BCD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF49B348-4188-4AC7-9ED4-5837F4B3BCD2}.Release|Any CPU.Build.0 = Release|Any CPU
{F16873D3-6511-48AA-B74E-8B13D5F20F20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F16873D3-6511-48AA-B74E-8B13D5F20F20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F16873D3-6511-48AA-B74E-8B13D5F20F20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F16873D3-6511-48AA-B74E-8B13D5F20F20}.Release|Any CPU.Build.0 = Release|Any CPU
{74C8ACD5-5DC4-4466-8846-B552FF131304}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74C8ACD5-5DC4-4466-8846-B552FF131304}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74C8ACD5-5DC4-4466-8846-B552FF131304}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -36,6 +42,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{54BE8DE8-08BD-429F-BCCA-3363A879D922} = {2A7200CA-F40B-4715-8726-4ED30C785FA4}
{BF49B348-4188-4AC7-9ED4-5837F4B3BCD2} = {2A7200CA-F40B-4715-8726-4ED30C785FA4}
{F16873D3-6511-48AA-B74E-8B13D5F20F20} = {2A7200CA-F40B-4715-8726-4ED30C785FA4}
{74C8ACD5-5DC4-4466-8846-B552FF131304} = {2A7200CA-F40B-4715-8726-4ED30C785FA4}
EndGlobalSection
EndGlobal

@ -25,11 +25,12 @@ public class ExercicesController : ControllerBase
//[Authorize]
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Create([FromBody] CreateExerciceTemplateDto dto)
{
//if (User.Identity.Name != "admin") return Forbid();
var exercice = _mapper.Map<Exercice>(dto);
var exercice = _mapper.Map<ExerciceTemplate>(dto);
_context.Exercices.Add(exercice);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetById), new { id = exercice.Id }, _mapper.Map<ExerciceTemplateDto>(exercice));
@ -37,6 +38,7 @@ public class ExercicesController : ControllerBase
//[Authorize]
[HttpPut("{id}")]
[AllowAnonymous]
public async Task<IActionResult> Update(string id, [FromBody] UpdateExerciceTemplateDto dto)
{
//if (User.Identity.Name != "admin") return Forbid();
@ -52,6 +54,7 @@ public class ExercicesController : ControllerBase
//[Authorize]
[HttpDelete("{id}")]
[AllowAnonymous]
public async Task<IActionResult> Delete(string id)
{
//if (User.Identity.Name != "admin") return Forbid();
@ -66,6 +69,7 @@ public class ExercicesController : ControllerBase
//[Authorize]
[HttpGet("{id}")]
[AllowAnonymous]
public async Task<ActionResult<ExerciceTemplateDto>> GetById(string id)
{
//if (User.Identity.Name != "admin") return Forbid();

@ -5,9 +5,6 @@ namespace CatalogService.DTOs;
public class UpdateExerciceTemplateDto
{
[Required]
public required string Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }

@ -9,5 +9,5 @@ public class CatalogDbContext : DbContext
{
}
public DbSet<Exercice> Exercices { get; set; }
public DbSet<ExerciceTemplate> Exercices { get; set; }
}

@ -22,9 +22,9 @@ public class DbInitializer
return;
}
var exercices = new List<Exercice>()
var exercices = new List<ExerciceTemplate>()
{
new Exercice
new ExerciceTemplate
{
Id = Guid.NewGuid().ToString(),
Name = "Squat",
@ -33,7 +33,7 @@ public class DbInitializer
ImageUrl = "images/squat.jpg",
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
},
new Exercice
new ExerciceTemplate
{
Id = Guid.NewGuid().ToString(),
Name = "Bench Press",
@ -42,7 +42,7 @@ public class DbInitializer
ImageUrl = "images/bench_press.jpg",
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
},
new Exercice
new ExerciceTemplate
{
Id = Guid.NewGuid().ToString(),
Name = "Deadlift",
@ -51,7 +51,7 @@ public class DbInitializer
ImageUrl = "images/deadlift.jpg",
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
},
new Exercice
new ExerciceTemplate
{
Id = Guid.NewGuid().ToString(),
Name = "Shoulder Press",
@ -60,7 +60,7 @@ public class DbInitializer
ImageUrl = "images/shoulder_press.jpg",
VideoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley",
},
new Exercice
new ExerciceTemplate
{
Id = Guid.NewGuid().ToString(),
Name = "Running on Treadmill",

@ -8,9 +8,9 @@ public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Exercice, ExerciceTemplateDto>();
CreateMap<ExerciceTemplateDto, Exercice>();
CreateMap<CreateExerciceTemplateDto, Exercice>();
CreateMap<UpdateExerciceTemplateDto, Exercice>();
CreateMap<ExerciceTemplate, ExerciceTemplateDto>();
CreateMap<ExerciceTemplateDto, ExerciceTemplate>();
CreateMap<CreateExerciceTemplateDto, ExerciceTemplate>();
CreateMap<UpdateExerciceTemplateDto, ExerciceTemplate>();
}
}

@ -3,7 +3,7 @@ using Shared.Enum;
namespace CatalogService.Entities;
public class Exercice : EntityBase
public class ExerciceTemplate : EntityBase
{
public string Name { get; set; }

@ -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,204 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TrainingSvc.Data;
#nullable disable
namespace TrainingSvc.Data.Migrations
{
[DbContext(typeof(TrainingDbContext))]
[Migration("20250607133126_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.15")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("CatalogService.Entities.ExerciceTemplate", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ImageUrl")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Target")
.HasColumnType("integer");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("VideoUrl")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ExerciceTemplates");
});
modelBuilder.Entity("TrainingSvc.Entities.ExerciceInstance", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<float>("Duration")
.HasColumnType("real");
b.Property<string>("ExerciceTemplateId")
.HasColumnType("text");
b.Property<bool>("IsDone")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NbReps")
.HasColumnType("integer");
b.Property<int>("NbSets")
.HasColumnType("integer");
b.Property<float>("RestingTime")
.HasColumnType("real");
b.Property<string>("SessionId")
.IsRequired()
.HasColumnType("text");
b.Property<float?>("Weight")
.HasColumnType("real");
b.HasKey("Id");
b.HasIndex("SessionId");
b.ToTable("ExerciceInstances");
});
modelBuilder.Entity("TrainingSvc.Entities.Session", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("Day")
.HasColumnType("integer");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("Target")
.HasColumnType("integer");
b.Property<string>("TrainingProgramId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("TrainingProgramId");
b.ToTable("Sessions");
});
modelBuilder.Entity("TrainingSvc.Entities.TrainingProgram", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("Difficulty")
.HasColumnType("integer");
b.Property<int>("Goal")
.HasColumnType("integer");
b.Property<string>("Lang")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NbDays")
.HasColumnType("integer");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("WeekDuration")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("TrainingPrograms");
});
modelBuilder.Entity("TrainingSvc.Entities.ExerciceInstance", b =>
{
b.HasOne("TrainingSvc.Entities.Session", "Session")
.WithMany("Exercices")
.HasForeignKey("SessionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Session");
});
modelBuilder.Entity("TrainingSvc.Entities.Session", b =>
{
b.HasOne("TrainingSvc.Entities.TrainingProgram", "TrainingProgram")
.WithMany("Sessions")
.HasForeignKey("TrainingProgramId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("TrainingProgram");
});
modelBuilder.Entity("TrainingSvc.Entities.Session", b =>
{
b.Navigation("Exercices");
});
modelBuilder.Entity("TrainingSvc.Entities.TrainingProgram", b =>
{
b.Navigation("Sessions");
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,126 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TrainingSvc.Data.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ExerciceTemplates",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", nullable: false),
Target = table.Column<int>(type: "integer", nullable: false),
ImageUrl = table.Column<string>(type: "text", nullable: false),
VideoUrl = table.Column<string>(type: "text", nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ExerciceTemplates", x => x.Id);
});
migrationBuilder.CreateTable(
name: "TrainingPrograms",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Lang = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", nullable: true),
WeekDuration = table.Column<int>(type: "integer", nullable: false),
NbDays = table.Column<int>(type: "integer", nullable: false),
OwnerId = table.Column<string>(type: "text", nullable: false),
Goal = table.Column<int>(type: "integer", nullable: false),
Difficulty = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TrainingPrograms", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Sessions",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Description = table.Column<string>(type: "text", nullable: true),
Day = table.Column<int>(type: "integer", nullable: false),
Target = table.Column<int>(type: "integer", nullable: true),
TrainingProgramId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Sessions", x => x.Id);
table.ForeignKey(
name: "FK_Sessions_TrainingPrograms_TrainingProgramId",
column: x => x.TrainingProgramId,
principalTable: "TrainingPrograms",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ExerciceInstances",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
ExerciceTemplateId = table.Column<string>(type: "text", nullable: true),
Duration = table.Column<float>(type: "real", nullable: false),
NbSets = table.Column<int>(type: "integer", nullable: false),
NbReps = table.Column<int>(type: "integer", nullable: false),
RestingTime = table.Column<float>(type: "real", nullable: false),
Weight = table.Column<float>(type: "real", nullable: true),
IsDone = table.Column<bool>(type: "boolean", nullable: false),
SessionId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ExerciceInstances", x => x.Id);
table.ForeignKey(
name: "FK_ExerciceInstances_Sessions_SessionId",
column: x => x.SessionId,
principalTable: "Sessions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ExerciceInstances_SessionId",
table: "ExerciceInstances",
column: "SessionId");
migrationBuilder.CreateIndex(
name: "IX_Sessions_TrainingProgramId",
table: "Sessions",
column: "TrainingProgramId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ExerciceInstances");
migrationBuilder.DropTable(
name: "ExerciceTemplates");
migrationBuilder.DropTable(
name: "Sessions");
migrationBuilder.DropTable(
name: "TrainingPrograms");
}
}
}

@ -0,0 +1,201 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TrainingSvc.Data;
#nullable disable
namespace TrainingSvc.Data.Migrations
{
[DbContext(typeof(TrainingDbContext))]
partial class TrainingDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.15")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("CatalogService.Entities.ExerciceTemplate", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ImageUrl")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Target")
.HasColumnType("integer");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("VideoUrl")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ExerciceTemplates");
});
modelBuilder.Entity("TrainingSvc.Entities.ExerciceInstance", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<float>("Duration")
.HasColumnType("real");
b.Property<string>("ExerciceTemplateId")
.HasColumnType("text");
b.Property<bool>("IsDone")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NbReps")
.HasColumnType("integer");
b.Property<int>("NbSets")
.HasColumnType("integer");
b.Property<float>("RestingTime")
.HasColumnType("real");
b.Property<string>("SessionId")
.IsRequired()
.HasColumnType("text");
b.Property<float?>("Weight")
.HasColumnType("real");
b.HasKey("Id");
b.HasIndex("SessionId");
b.ToTable("ExerciceInstances");
});
modelBuilder.Entity("TrainingSvc.Entities.Session", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("Day")
.HasColumnType("integer");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("Target")
.HasColumnType("integer");
b.Property<string>("TrainingProgramId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("TrainingProgramId");
b.ToTable("Sessions");
});
modelBuilder.Entity("TrainingSvc.Entities.TrainingProgram", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("Difficulty")
.HasColumnType("integer");
b.Property<int>("Goal")
.HasColumnType("integer");
b.Property<string>("Lang")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("NbDays")
.HasColumnType("integer");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("WeekDuration")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("TrainingPrograms");
});
modelBuilder.Entity("TrainingSvc.Entities.ExerciceInstance", b =>
{
b.HasOne("TrainingSvc.Entities.Session", "Session")
.WithMany("Exercices")
.HasForeignKey("SessionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Session");
});
modelBuilder.Entity("TrainingSvc.Entities.Session", b =>
{
b.HasOne("TrainingSvc.Entities.TrainingProgram", "TrainingProgram")
.WithMany("Sessions")
.HasForeignKey("TrainingProgramId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("TrainingProgram");
});
modelBuilder.Entity("TrainingSvc.Entities.Session", b =>
{
b.Navigation("Exercices");
});
modelBuilder.Entity("TrainingSvc.Entities.TrainingProgram", b =>
{
b.Navigation("Sessions");
});
#pragma warning restore 612, 618
}
}
}

@ -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,15 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:7002",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

@ -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…
Cancel
Save