diff --git a/.gitignore b/.gitignore
index 85546c5..c985bbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -549,4 +549,19 @@ xcuserdata/
*.xcscmblueprint
*.xccheckout
+
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/.idea.HeartTrack.iml
+/modules.xml
+/projectSettingsUpdater.xml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+.ideaMigration/
Migration/
\ No newline at end of file
diff --git a/src/ApiMappeur/ActivityMappeur.cs b/src/ApiMappeur/ActivityMappeur.cs
new file mode 100644
index 0000000..fb10d7b
--- /dev/null
+++ b/src/ApiMappeur/ActivityMappeur.cs
@@ -0,0 +1,49 @@
+using Dto;
+using Model;
+
+namespace ApiMappeur;
+
+public static class ActivityMappeur
+{
+ /*public static ActivityDto ToDto(this Activity activity)
+ {
+ return new ActivityDto
+ {
+ Id = activity.Id,
+ Name = activity.Name,
+ Type = activity.Type,
+ Date = activity.Date,
+ Duration = activity.EndTime - activity.StartTime,
+ Distance = activity.Distance,
+ Elevation = activity.Elevation,
+ AverageSpeed = activity.AverageSpeed,
+ AverageHeartRate = activity.AverageHeartRate,
+ Calories = activity.Calories,
+ Description = activity.Description,
+ Gpx = activity.Gpx,
+ Image = activity.Image,
+ AthleteId = activity.AthleteId
+ };
+ }
+
+ public static Activity ToModel(this ActivityDto activityDto)
+ {
+ return new Activity
+ {
+ Id = activityDto.Id,
+ Name = activityDto.Name,
+ Type = activityDto.Type,
+ Date = activityDto.Date,
+ Duration = activityDto.Duration,
+ Distance = activityDto.Distance,
+ Elevation = activityDto.Elevation,
+ AverageSpeed = activityDto.AverageSpeed,
+ AverageHeartRate = activityDto.AverageHeartRate,
+ Calories = activityDto.Calories,
+ Description = activityDto.Description,
+ Gpx = activityDto.Gpx,
+ Image = activityDto.Image,
+ AthleteId = activityDto.AthleteId
+ };
+ }*/
+}
\ No newline at end of file
diff --git a/src/ApiMappeur/ApiMappeur.csproj b/src/ApiMappeur/ApiMappeur.csproj
new file mode 100644
index 0000000..7ba11f5
--- /dev/null
+++ b/src/ApiMappeur/ApiMappeur.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/src/ApiMappeur/AthleteMappeur.cs b/src/ApiMappeur/AthleteMappeur.cs
new file mode 100644
index 0000000..1021e13
--- /dev/null
+++ b/src/ApiMappeur/AthleteMappeur.cs
@@ -0,0 +1,59 @@
+using Dto;
+using Model;
+
+namespace ApiMappeur;
+
+public static class UserMappeur
+{
+ public static UserDto ToDto(this User user)
+ {
+ return new UserDto
+ {
+ Id = user.Id,
+ Username = user.Username,
+ ProfilePicture = user.ProfilePicture,
+ LastName = user.LastName,
+ FirstName = user.FirstName,
+ Email = user.Email,
+ Password = user.MotDePasse,
+ Sexe = user.Sexe,
+ Lenght = user.Lenght,
+ Weight = user.Weight,
+ DateOfBirth = user.DateOfBirth,
+ IsCoach = user.Role is Coach
+ };
+
+ }
+
+ public static User ToModel(this UserDto userDto)
+ {
+ if (userDto.IsCoach)
+ {
+ return new User(
+ userDto.Username,
+ userDto.ProfilePicture,
+ userDto.LastName,
+ userDto.FirstName,
+ userDto.Email,
+ userDto.Password,
+ userDto.Sexe,
+ userDto.Lenght,
+ userDto.Weight,
+ userDto.DateOfBirth,
+ new Coach()
+ );
+ }
+ return new User(
+ userDto.Username,
+ userDto.ProfilePicture,
+ userDto.LastName,
+ userDto.FirstName,
+ userDto.Email,
+ userDto.Password,
+ userDto.Sexe,
+ userDto.Lenght,
+ userDto.Weight,
+ userDto.DateOfBirth,
+ new Athlete());
+ }
+}
\ No newline at end of file
diff --git a/src/Dto/ActivityDto.cs b/src/Dto/ActivityDto.cs
new file mode 100644
index 0000000..d4bf5f1
--- /dev/null
+++ b/src/Dto/ActivityDto.cs
@@ -0,0 +1,19 @@
+namespace Dto;
+
+public class ActivityDto
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Type { get; set; }
+ public DateTime Date { get; set; }
+ public TimeSpan Duration { get; set; }
+ public float Distance { get; set; }
+ public float Elevation { get; set; }
+ public float AverageSpeed { get; set; }
+ public int AverageHeartRate { get; set; }
+ public int Calories { get; set; }
+ public string Description { get; set; }
+ public string? Gpx { get; set; }
+ public string? Image { get; set; }
+ public int AthleteId { get; set; }
+}
\ No newline at end of file
diff --git a/src/Dto/AthleteDto.cs b/src/Dto/AthleteDto.cs
new file mode 100644
index 0000000..9ee369d
--- /dev/null
+++ b/src/Dto/AthleteDto.cs
@@ -0,0 +1,23 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Dto;
+
+public class UserDto
+{
+ public int Id { get; set; }
+ [MaxLength(100)]
+ public required string Username { get; set; }
+ [MaxLength(150)]
+ public required string LastName { get; set; }
+ [MaxLength(100)]
+ public required string FirstName { get; set; }
+ public required string Email { get; set; }
+ public required string Sexe { get; set; }
+ public float Lenght { get; set; }
+ public float Weight { get; set; }
+ public string? Password { get; set; }
+ public DateTime DateOfBirth { get; set; }
+
+ public string ProfilePicture { get; set; } = "default.jpg";
+ public bool IsCoach { get; set; }
+}
diff --git a/src/Dto/DataSourceDto.cs b/src/Dto/DataSourceDto.cs
new file mode 100644
index 0000000..e69de29
diff --git a/src/Dto/Dto.csproj b/src/Dto/Dto.csproj
new file mode 100644
index 0000000..bb23fb7
--- /dev/null
+++ b/src/Dto/Dto.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/src/Dto/HeartRateDto.cs b/src/Dto/HeartRateDto.cs
new file mode 100644
index 0000000..e69de29
diff --git a/src/Dto/NotificationDto.cs b/src/Dto/NotificationDto.cs
new file mode 100644
index 0000000..e69de29
diff --git a/src/Dto/StatisticDto.cs b/src/Dto/StatisticDto.cs
new file mode 100644
index 0000000..e69de29
diff --git a/src/Dto/TrainingDto.cs b/src/Dto/TrainingDto.cs
new file mode 100644
index 0000000..e69de29
diff --git a/src/HeartTrack.sln b/src/HeartTrack.sln
index 0ae03f5..2ec18e7 100644
--- a/src/HeartTrack.sln
+++ b/src/HeartTrack.sln
@@ -15,6 +15,26 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestEntities", "Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestRelationships", "Tests\ConsoleTestRelationships\ConsoleTestRelationships.csproj", "{2D166FAD-4934-474B-96A8-6C0635156EC2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dto", "Dto\Dto.csproj", "{562019BC-0F61-41B0-9BAE-259B92C6BFBA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeartTrackAPI", "HeartTrackAPI\HeartTrackAPI.csproj", "{C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestsAPI", "TestsAPI", "{30FC2BE9-7397-445A-84AD-043CE70F4281}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsXUnit", "Tests\TestsAPI\TestsXUnit\TestsXUnit.csproj", "{44C367DC-5FE0-4CF2-9E76-A0282E931853}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiMappeur", "ApiMappeur\ApiMappeur.csproj", "{CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubAPI", "StubAPI\StubAPI.csproj", "{B9679DCA-F4C8-45BE-A849-44E2BA814083}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsEntities", "Tests\UnitTestsEntities\UnitTestsEntities.csproj", "{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedEF", "SharedEF\SharedEF.csproj", "{55478079-0AA0-47C1-97FF-A048091FD930}"
@@ -50,6 +70,42 @@ Global
{2D166FAD-4934-474B-96A8-6C0635156EC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D166FAD-4934-474B-96A8-6C0635156EC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D166FAD-4934-474B-96A8-6C0635156EC2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {562019BC-0F61-41B0-9BAE-259B92C6BFBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {562019BC-0F61-41B0-9BAE-259B92C6BFBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {562019BC-0F61-41B0-9BAE-259B92C6BFBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {562019BC-0F61-41B0-9BAE-259B92C6BFBA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {44C367DC-5FE0-4CF2-9E76-A0282E931853}.Release|Any CPU.Build.0 = Release|Any CPU
+ {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {30AB7FAA-6072-40B6-A15E-9188B59144F9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CB142F6B-0FF1-45B3-AB46-6F8DCD096C20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B9679DCA-F4C8-45BE-A849-44E2BA814083}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Release|Any CPU.Build.0 = Release|Any CPU
{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -66,6 +122,10 @@ Global
GlobalSection(NestedProjects) = preSolution
{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{2D166FAD-4934-474B-96A8-6C0635156EC2} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
+ {30FC2BE9-7397-445A-84AD-043CE70F4281} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
+ {9E4D3AC5-E6CA-4753-BD96-BF5EE793931A} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
+ {44C367DC-5FE0-4CF2-9E76-A0282E931853} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
+ {E515C8B6-6282-4D8B-8523-7B3A13E4AF58} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
EndGlobalSection
EndGlobal
diff --git a/src/HeartTrackAPI/Controllers/ActivityController.cs b/src/HeartTrackAPI/Controllers/ActivityController.cs
new file mode 100644
index 0000000..1848ca9
--- /dev/null
+++ b/src/HeartTrackAPI/Controllers/ActivityController.cs
@@ -0,0 +1,99 @@
+using ApiMappeur;
+using Dto;
+using HeartTrackAPI.Request;
+using HeartTrackAPI.Responce;
+using Microsoft.AspNetCore.Mvc;
+using Shared;
+using Model;
+using Model.Repository;
+
+namespace HeartTrackAPI.Controllers;
+[ApiController]
+[Route("api/activities")]
+public class ActivityController : Controller
+{
+ private readonly IActivityRepository _activityService;
+ private readonly ILogger _logger;
+
+ public ActivityController(IActivityRepository activityService, ILogger logger)
+ {
+ _activityService = activityService;
+ _logger = logger;
+ }
+/*
+ [HttpGet]
+ [ProducesResponseType(typeof(PageResponse), 200)]
+ [ProducesResponseType(400)]
+ [ProducesResponseType(500)]
+ public async Task>> GetActivities([FromQuery] PageRequest pageRequest)
+ {
+ try
+ {
+ var totalCount = await _activityService.GetNbItems();
+ if (pageRequest.Count * pageRequest.Index >= totalCount)
+ {
+ _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount);
+ return BadRequest("To many object is asked the max is : " + totalCount);
+ }
+ _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivities), pageRequest);
+ var activities = await _activityService.GetActivities(pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false);
+ var pageResponse = new PageResponse(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto()));
+ return Ok(pageResponse);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error while getting all activities");
+ return StatusCode(500);
+ }
+ }
+
+ [HttpGet("{id}")]
+ public async Task> GetActivity(int id)
+ {
+ var activity = await _activityService.GetActivityByIdAsync(id);
+ if (activity == null)
+ {
+ return NotFound();
+ }
+ return Ok(activity.ToDto());
+ }
+
+ [HttpPost]
+ public async Task> PostActivity(ActivityDto activityDto)
+ {
+ var activity = activityDto.ToModel();
+ var result = await _activityService.AddActivity(activity);
+ if (result == null)
+ {
+ return BadRequest();
+ }
+ return CreatedAtAction(nameof(GetActivity), new { id = result.Id }, result.ToDto());
+ }
+
+ [HttpPut("{id}")]
+ public async Task PutActivity(int id, ActivityDto activityDto)
+ {
+ if (id != activityDto.Id)
+ {
+ return BadRequest();
+ }
+ var activity = activityDto.ToModel();
+ var result = await _activityService.UpdateActivity(id, activity);
+ if (result == null)
+ {
+ return NotFound();
+ }
+ return NoContent();
+ }
+
+ [HttpDelete("{id}")]
+ public async Task DeleteActivity(int id)
+ {
+ var result = await _activityService.DeleteActivity(id);
+ if (!result)
+ {
+ return NotFound();
+ }
+ return NoContent();
+ }*/
+}
\ No newline at end of file
diff --git a/src/HeartTrackAPI/Controllers/UsersController.cs b/src/HeartTrackAPI/Controllers/UsersController.cs
new file mode 100644
index 0000000..7b57283
--- /dev/null
+++ b/src/HeartTrackAPI/Controllers/UsersController.cs
@@ -0,0 +1,160 @@
+using ApiMappeur;
+using Dto;
+using HeartTrackAPI.Request;
+using HeartTrackAPI.Responce;
+using Microsoft.AspNetCore.Mvc;
+using Model;
+using Model.Manager;
+using Model.Repository;
+using Shared;
+
+namespace HeartTrackAPI.Controllers;
+
+[ApiController]
+[Route("api/users")]
+public class UsersController : Controller
+{
+ private readonly ILogger _logger;
+ private readonly IUserRepository _userService;
+ public UsersController(ILogger logger, IDataManager dataManager)
+ {
+ _logger = logger;
+ _userService = dataManager.UserRepository;
+ }
+
+ [HttpGet]
+ [ProducesResponseType(typeof(PageResponse), 200)]
+ [ProducesResponseType(400)]
+ [ProducesResponseType(500)]
+ public async Task>> Get([FromQuery] PageRequest request)
+ {
+ try
+ {
+ var totalCount = await _userService.GetNbItems();
+ if (request.Count * request.Index >= totalCount)
+ {
+ _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount);
+ return BadRequest("To many object is asked the max is : " + totalCount);
+ }
+
+ _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), null);
+
+ var athletes = await _userService.GetUsers(request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false);
+ var pageResponse = new PageResponse(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto()));
+ return Ok(pageResponse);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error while getting all athletes");
+ return StatusCode(500);
+ }
+ }
+
+ [HttpGet("{id}")]
+ [ProducesResponseType(typeof(UserDto), 200)]
+ [ProducesResponseType(404)]
+ [ProducesResponseType(500)]
+ public async Task> GetById(int id)
+ {
+ try
+ {
+ _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetById), id);
+ var athlete = await _userService.GetItemById(id);
+ if (athlete == null)
+ {
+ _logger.LogError("Athlete with id {id} not found", id);
+ return NotFound($"Athlete with id {id} not found");
+ }
+ return Ok(athlete.ToDto());
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error while getting athlete by id {id}", id);
+ return StatusCode(500);
+ }
+ }
+
+
+ [HttpGet("count")]
+ [ProducesResponseType(typeof(int), 200)]
+ [ProducesResponseType(500)]
+ public async Task> Count()
+ {
+ try
+ {
+ _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Count), null);
+ var nbUsers = await _userService.GetNbItems();
+ return Ok(nbUsers);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error while getting the number of users");
+ return StatusCode(500);
+ }
+ }
+
+ [HttpPut("{id}")]
+ [ProducesResponseType(typeof(UserDto), 200)]
+ [ProducesResponseType(404)]
+ [ProducesResponseType(500)]
+ public async Task> Update(int id, [FromBody] UserDto user)
+ {
+ try
+ {
+ _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Update), user,id);
+ var athlete = await _userService.GetItemById(id);
+ if (athlete == null)
+ {
+ _logger.LogError("Athlete with id {id} not found", id);
+ return NotFound($"Athlete with id {id} not found");
+ }
+ var updatedAthlete = await _userService.UpdateItem(id, user.ToModel());
+ if(updatedAthlete == null)
+ {
+ _logger.LogError("Error while updating athlete with id {id}", id);
+ return StatusCode(500);
+ }
+ return Ok(updatedAthlete.ToDto());
+
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error while getting the number of users");
+ return StatusCode(500);
+ }
+ }
+
+ [HttpDelete("{id}")]
+ [ProducesResponseType(200)]
+ [ProducesResponseType(404)]
+ [ProducesResponseType(500)]
+ public async Task Delete(int id)
+ {
+ try
+ {
+ _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Delete), null,id);
+
+
+ var athlete = await _userService.GetItemById(id);
+ if (athlete == null)
+ {
+ _logger.LogError("Athlete with id {id} not found", id);
+ return NotFound($"Athlete with id {id} not found");
+ }
+ var isDeleted = await _userService.DeleteItem(id);
+ if(!isDeleted)
+ {
+ _logger.LogError("Error while deleting athlete with id {id}", id);
+ return StatusCode(500);
+ }
+ return Ok();
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error while getting the number of users");
+ return StatusCode(500);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/HeartTrackAPI/HeartTrackAPI.csproj b/src/HeartTrackAPI/HeartTrackAPI.csproj
new file mode 100644
index 0000000..f24f646
--- /dev/null
+++ b/src/HeartTrackAPI/HeartTrackAPI.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\..\.nuget\packages\newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll
+
+
+
+
diff --git a/src/HeartTrackAPI/HeartTrackAPI.http b/src/HeartTrackAPI/HeartTrackAPI.http
new file mode 100644
index 0000000..97c9a67
--- /dev/null
+++ b/src/HeartTrackAPI/HeartTrackAPI.http
@@ -0,0 +1,6 @@
+@HeartTrackAPI_HostAddress = http://localhost:5030
+
+GET {{HeartTrackAPI_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/src/HeartTrackAPI/Program.cs b/src/HeartTrackAPI/Program.cs
new file mode 100644
index 0000000..b396e74
--- /dev/null
+++ b/src/HeartTrackAPI/Program.cs
@@ -0,0 +1,26 @@
+using Model;
+using Model.Manager;
+using Model.Repository;
+using StubAPI;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+builder.Services.AddControllers();
+builder.Services.AddSingleton();
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+app.MapControllers();
+app.Run();
\ No newline at end of file
diff --git a/src/HeartTrackAPI/Properties/launchSettings.json b/src/HeartTrackAPI/Properties/launchSettings.json
new file mode 100644
index 0000000..7156f3d
--- /dev/null
+++ b/src/HeartTrackAPI/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:13881",
+ "sslPort": 44333
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5030",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7233;http://localhost:5030",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/HeartTrackAPI/Request/PageRequest.cs b/src/HeartTrackAPI/Request/PageRequest.cs
new file mode 100644
index 0000000..bc04934
--- /dev/null
+++ b/src/HeartTrackAPI/Request/PageRequest.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+using Newtonsoft.Json.Converters;
+using Shared;
+
+namespace HeartTrackAPI.Request;
+
+public class PageRequest
+{
+
+ public string? OrderingPropertyName { get; set; } = null;// need to be map on the dto OrderCriteria
+ public bool? Descending { get; set; } = false;
+ public int Index { get; set; } = 0;
+ public int Count { get; set; } = 5;
+}
diff --git a/src/HeartTrackAPI/Responce/PageResponse.cs b/src/HeartTrackAPI/Responce/PageResponse.cs
new file mode 100644
index 0000000..93b4cfe
--- /dev/null
+++ b/src/HeartTrackAPI/Responce/PageResponse.cs
@@ -0,0 +1,22 @@
+namespace HeartTrackAPI.Responce;
+
+public class PageResponse
+{
+ // The index of the first item
+ public int Index { get; set; } = 1;
+ // The number of items
+ public int Count { get; set; } = 1;
+ // The total number of items
+ public int Total { get; set; } = 1;
+ // The items
+ public IEnumerable Items { get; set; }
+
+ public PageResponse(int index, int count, int total, IEnumerable items)
+ {
+ Index = index;
+ Count = count;
+ Total = total;
+ Items = items;
+ }
+
+}
\ No newline at end of file
diff --git a/src/HeartTrackAPI/appsettings.Development.json b/src/HeartTrackAPI/appsettings.Development.json
new file mode 100644
index 0000000..ff66ba6
--- /dev/null
+++ b/src/HeartTrackAPI/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/HeartTrackAPI/appsettings.json b/src/HeartTrackAPI/appsettings.json
new file mode 100644
index 0000000..4d56694
--- /dev/null
+++ b/src/HeartTrackAPI/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/Model/Activity.cs b/src/Model/Activity.cs
new file mode 100644
index 0000000..06801bf
--- /dev/null
+++ b/src/Model/Activity.cs
@@ -0,0 +1,68 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Model;
+public class Activity
+ {
+ public int Id { get; set; }
+ public string Type { get; set; }
+ public DateTime Date { get; set; }
+ public DateTime StartTime { get; set; }
+ public DateTime EndTime { get; set; }
+
+ private int _effort;
+ [Range(0, 5)]
+ public int Effort
+ {
+ get => _effort;
+ set
+ {
+ if (value < 0 || value > 5)
+ {
+ throw new ArgumentException("Effort must be between 0 and 5.");
+ }
+ _effort = value;
+ }
+ }
+ public float Variability { get; set; }
+ public float Variance { get; set; }
+ public float StandardDeviation { get; set; }
+ public float Average { get; set; }
+ public int Maximum { get; set; }
+ public int Minimum { get; set; }
+ public float AverageTemperature { get; set; }
+ public bool HasAutoPause { get; set; }
+
+ public Activity(int idActivity ,string type, DateTime date, DateTime startTime, DateTime endTime,
+ int effort, float variability, float variance, float standardDeviation,
+ float average, int maximum, int minimum, float averageTemperature, bool hasAutoPause)
+ {
+ Id = idActivity;
+ Type = type;
+ Date = date;
+ StartTime = startTime;
+ EndTime = endTime;
+ Effort = effort;
+ Variability = variability;
+ Variance = variance;
+ StandardDeviation = standardDeviation;
+ Average = average;
+ Maximum = maximum;
+ Minimum = minimum;
+ AverageTemperature = averageTemperature;
+ HasAutoPause = hasAutoPause;
+ }
+ public Activity(){}
+
+ public override string ToString()
+ {
+ return $"Activity #{Id}: {Type} on {Date:d/M/yyyy} from {StartTime:HH:mm:ss} to {EndTime:HH:mm:ss}" +
+ $" with an effort of {Effort}/5 and an average temperature of {AverageTemperature}°C" +
+ $" and a heart rate variability of {Variability} bpm" +
+ $" and a variance of {Variance} bpm" +
+ $" and a standard deviation of {StandardDeviation} bpm" +
+ $" and an average of {Average} bpm" +
+ $" and a maximum of {Maximum} bpm" +
+ $" and a minimum of {Minimum} bpm" +
+ $" and auto pause is {(HasAutoPause ? "enabled" : "disabled")}.";
+ }
+ }
\ No newline at end of file
diff --git a/src/Model/Athlete.cs b/src/Model/Athlete.cs
new file mode 100644
index 0000000..fa04950
--- /dev/null
+++ b/src/Model/Athlete.cs
@@ -0,0 +1,9 @@
+namespace Model;
+
+public class Athlete : Role
+{
+ public override bool CheckAdd(User user)
+ {
+ return user.Role is Athlete;
+ }
+}
\ No newline at end of file
diff --git a/src/Model/Coach.cs b/src/Model/Coach.cs
new file mode 100644
index 0000000..84fd18c
--- /dev/null
+++ b/src/Model/Coach.cs
@@ -0,0 +1,10 @@
+namespace Model;
+
+public class Coach : Role
+{
+ public override bool CheckAdd(User user)
+ {
+ return user.Role is Athlete;
+ }
+
+}
\ No newline at end of file
diff --git a/src/Model/EnumMappeur.cs b/src/Model/EnumMappeur.cs
new file mode 100644
index 0000000..b939ee9
--- /dev/null
+++ b/src/Model/EnumMappeur.cs
@@ -0,0 +1,24 @@
+using Model.Repository;
+
+namespace Model;
+
+public static class EnumMappeur
+{
+ public static Shared.AthleteOrderCriteria ToEnum(this IUserRepository repository, string? value)
+ {
+ return value switch
+ {
+ "None" => Shared.AthleteOrderCriteria.None,
+ "ByUsername" => Shared.AthleteOrderCriteria.ByUsername,
+ "ByFirstName" => Shared.AthleteOrderCriteria.ByFirstName,
+ "ByLastName" => Shared.AthleteOrderCriteria.ByLastName,
+ "BySexe" => Shared.AthleteOrderCriteria.BySexe,
+ "ByLenght" => Shared.AthleteOrderCriteria.ByLenght,
+ "ByWeight" => Shared.AthleteOrderCriteria.ByWeight,
+ "ByDateOfBirth" => Shared.AthleteOrderCriteria.ByDateOfBirth,
+ "ByEmail" => Shared.AthleteOrderCriteria.ByEmail,
+ "ByIsCoach" => Shared.AthleteOrderCriteria.ByIsCoach,
+ _ => Shared.AthleteOrderCriteria.None
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Model/Manager/IDataManager.cs b/src/Model/Manager/IDataManager.cs
new file mode 100644
index 0000000..277ea0b
--- /dev/null
+++ b/src/Model/Manager/IDataManager.cs
@@ -0,0 +1,9 @@
+using Model.Repository;
+
+namespace Model.Manager;
+
+public interface IDataManager
+{
+ IUserRepository UserRepository { get; }
+ IActivityRepository ActivityRepository { get; }
+}
diff --git a/src/Model/Model.csproj b/src/Model/Model.csproj
new file mode 100644
index 0000000..18de4eb
--- /dev/null
+++ b/src/Model/Model.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Model/Notification.cs b/src/Model/Notification.cs
new file mode 100644
index 0000000..8a2617a
--- /dev/null
+++ b/src/Model/Notification.cs
@@ -0,0 +1,6 @@
+namespace Model;
+
+public class Notification
+{
+
+}
\ No newline at end of file
diff --git a/src/Model/RelationshipRequest.cs b/src/Model/RelationshipRequest.cs
new file mode 100644
index 0000000..9024b50
--- /dev/null
+++ b/src/Model/RelationshipRequest.cs
@@ -0,0 +1,6 @@
+namespace Model;
+
+public class RelationshipRequest
+{
+
+}
\ No newline at end of file
diff --git a/src/Model/Repository/IActivityRepository.cs b/src/Model/Repository/IActivityRepository.cs
new file mode 100644
index 0000000..258bbd9
--- /dev/null
+++ b/src/Model/Repository/IActivityRepository.cs
@@ -0,0 +1,13 @@
+using Shared;
+
+namespace Model.Repository;
+
+public interface IActivityRepository
+{
+ public Task> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false);
+ public Task GetActivityByIdAsync(int id);
+ public Task AddActivity(Activity activity);
+ public Task UpdateActivity(int id, Activity activity);
+ public Task DeleteActivity(int id);
+ public Task GetNbItems();
+}
\ No newline at end of file
diff --git a/src/Model/Repository/IUserRepository.cs b/src/Model/Repository/IUserRepository.cs
new file mode 100644
index 0000000..d6d528c
--- /dev/null
+++ b/src/Model/Repository/IUserRepository.cs
@@ -0,0 +1,8 @@
+using Shared;
+
+namespace Model.Repository;
+
+public interface IUserRepository : IGenericRepository
+{
+ public Task> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false);
+}
\ No newline at end of file
diff --git a/src/Model/Role.cs b/src/Model/Role.cs
new file mode 100644
index 0000000..5353ea7
--- /dev/null
+++ b/src/Model/Role.cs
@@ -0,0 +1,42 @@
+namespace Model;
+
+public abstract class Role
+{
+ protected List UsersList { get; set; } = [];
+ protected List UsersRequests { get; set; } = [];
+ protected List TrainingList { get; set; } = [];
+ public abstract bool CheckAdd(User user);
+
+ public bool AddUser(User user)
+ {
+ if (!CheckAdd(user)) return false;
+ UsersList.Add(user);
+ return true;
+ }
+
+ public bool RemoveUser(User user)
+ {
+ return UsersList.Remove(user);
+ }
+
+ public void AddTraining(Training training)
+ {
+ TrainingList.Add(training);
+ }
+
+ public bool RemoveTraining(Training training)
+ {
+ return TrainingList.Remove(training);
+ }
+
+ public void AddUserRequest(RelationshipRequest request)
+ {
+ UsersRequests.Add(request);
+ }
+
+ public bool RemoveUserRequest(RelationshipRequest request)
+ {
+ return UsersRequests.Remove(request);
+
+ }
+}
\ No newline at end of file
diff --git a/src/Model/Training.cs b/src/Model/Training.cs
new file mode 100644
index 0000000..164afbb
--- /dev/null
+++ b/src/Model/Training.cs
@@ -0,0 +1,6 @@
+namespace Model;
+
+public class Training
+{
+
+}
\ No newline at end of file
diff --git a/src/Model/User.cs b/src/Model/User.cs
new file mode 100644
index 0000000..6efffa8
--- /dev/null
+++ b/src/Model/User.cs
@@ -0,0 +1,41 @@
+namespace Model;
+
+public class User
+{
+ public int Id { get; set; }
+ public string Username { get; set; }
+ public string ProfilePicture { get; set; }
+ public string LastName { get; set; }
+ public string FirstName { get; set; }
+ public string Email { get; set; }
+ public string MotDePasse { get; set; }
+ public string Sexe { get; set; }
+ public float Lenght { get; set; }
+ public float Weight { get; set; }
+ public DateTime DateOfBirth { get; set; }
+ public Role Role { get; set; }
+
+ protected List Notifications { get; set; } = new List();
+
+ public User( string username, string profilePicture, string nom, string prenom, string email, string motDePasse, string sexe, float taille, float poids, DateTime dateNaissance, Role role)
+ {
+ Username = username;
+ ProfilePicture = profilePicture;
+ LastName = nom;
+ FirstName = prenom;
+ Email = email;
+ MotDePasse = motDePasse;
+ Sexe = sexe;
+ Lenght = taille;
+ Weight = poids;
+ DateOfBirth = dateNaissance;
+ Role = role;
+ }
+ public User(){}
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/Shared/ActivityOrderCriteria.cs b/src/Shared/ActivityOrderCriteria.cs
new file mode 100644
index 0000000..f1e30ed
--- /dev/null
+++ b/src/Shared/ActivityOrderCriteria.cs
@@ -0,0 +1,16 @@
+namespace Shared;
+
+public enum ActivityOrderCriteria
+{
+ None,
+ ByName,
+ ByType,
+ ByDate,
+ ByDuration,
+ ByDistance,
+ ByElevation,
+ ByAverageSpeed,
+ ByAverageHeartRate,
+ ByCalories,
+ ByDescription
+}
\ No newline at end of file
diff --git a/src/Shared/AthleteOrderCriteria.cs b/src/Shared/AthleteOrderCriteria.cs
new file mode 100644
index 0000000..4bc54f0
--- /dev/null
+++ b/src/Shared/AthleteOrderCriteria.cs
@@ -0,0 +1,45 @@
+namespace Shared
+{
+ public enum AthleteOrderCriteria
+ {
+ None,
+ ByUsername,
+ ByFirstName,
+ ByLastName,
+ BySexe,
+ ByLenght,
+ ByWeight,
+ ByDateOfBirth,
+ ByEmail,
+ ByIsCoach
+ }
+
+}
+
+
+/*public AthleteOrderCriteria MapToAthleteOrderCriteria(string orderingPropertyName)
+ {
+ switch (orderingPropertyName)
+ {
+ case nameof(User.Username):
+ return AthleteOrderCriteria.ByUsername;
+ case nameof(User.FirstName):
+ return AthleteOrderCriteria.ByFirstName;
+ case nameof(User.LastName):
+ return AthleteOrderCriteria.ByLastName;
+ case nameof(User.Sexe):
+ return AthleteOrderCriteria.BySexe;
+ case nameof(User.Length):
+ return AthleteOrderCriteria.ByLength;
+ case nameof(User.Weight):
+ return AthleteOrderCriteria.ByWeight;
+ case nameof(User.DateOfBirth):
+ return AthleteOrderCriteria.ByDateOfBirth;
+ case nameof(User.Email):
+ return AthleteOrderCriteria.ByEmail;
+ case nameof(User.IsCoach):
+ return AthleteOrderCriteria.ByIsCoach;
+ default:
+ return AthleteOrderCriteria.None;
+ }
+ }*/
\ No newline at end of file
diff --git a/src/Shared/IGenericRepository.cs b/src/Shared/IGenericRepository.cs
new file mode 100644
index 0000000..372d79b
--- /dev/null
+++ b/src/Shared/IGenericRepository.cs
@@ -0,0 +1,12 @@
+namespace Shared;
+
+public interface IGenericRepository
+{
+ Task> GetItems(int index, int count, string? orderingProperty = null, bool descending = false);
+ Task GetItemById(int id);
+ Task UpdateItem(int oldItem, T newItem);
+ Task AddItem(T item);
+ Task DeleteItem(int item);
+ Task GetNbItems();
+
+}
\ No newline at end of file
diff --git a/src/Shared/Shared.csproj b/src/Shared/Shared.csproj
new file mode 100644
index 0000000..7d742ab
--- /dev/null
+++ b/src/Shared/Shared.csproj
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/src/StubAPI/ActivityService.cs b/src/StubAPI/ActivityService.cs
new file mode 100644
index 0000000..0e87004
--- /dev/null
+++ b/src/StubAPI/ActivityService.cs
@@ -0,0 +1,38 @@
+using Model;
+using Model.Repository;
+using Shared;
+
+namespace StubAPI;
+
+public class ActivityService: IActivityRepository
+{
+ public async Task> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task GetActivityByIdAsync(int id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task AddActivity(Activity activity)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task UpdateActivity(int id, Activity activity)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task DeleteActivity(int id)
+ {
+ throw new NotImplementedException();
+ }
+
+ public async Task GetNbItems()
+ {
+ throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/StubAPI/AthleteService.cs b/src/StubAPI/AthleteService.cs
new file mode 100644
index 0000000..b1b1a28
--- /dev/null
+++ b/src/StubAPI/AthleteService.cs
@@ -0,0 +1,72 @@
+using Model;
+using Model.Repository;
+using Shared;
+
+namespace StubAPI;
+
+public class UserService : IUserRepository
+{
+ private List athletes =
+ [
+ new User
+ {
+ Id = 1, Username = "DoeDoe", ProfilePicture = "https://images.unsplash.com/photo-1682687982134-2ac563b2228b?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", FirstName = "John", LastName = "Doe",
+ Sexe = "M", Lenght = 180, Weight = 70, DateOfBirth = new DateTime(1990, 1, 1),
+ Email = "john.doe@example.com", Role = new Athlete()
+ },
+
+ new User
+ {
+ Id = 2, Username = "SmithSmith", ProfilePicture = "https://images.unsplash.com/photo-1709507779917-242b560288be?q=80&w=2080&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", FirstName = "Jane", LastName = "Smith",
+ Sexe = "F", Lenght = 170, Weight = 60, DateOfBirth = new DateTime(1992, 2, 2),
+ Email = "athlete2@example.com", Role = new Coach()
+ },
+
+ new User
+ {
+ Id = 3, Username = "Athlete3", ProfilePicture = "https://plus.unsplash.com/premium_photo-1705091981693-6006f8a20479?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", FirstName = "First3", LastName = "Last3",
+ Sexe = "M", Lenght = 190, Weight = 80, DateOfBirth = new DateTime(1994, 3, 3), Email = "ath@ex.fr",
+ Role = new Athlete()
+ }
+
+ ];
+
+ public async Task> GetUsers(int index, int count, AthleteOrderCriteria? orderingProperty = null, bool descending = false)
+ => athletes.GetItemsWithFilterAndOrdering(c=>true,index, count,orderingProperty != AthleteOrderCriteria.None ? orderingProperty: null , descending);
+
+ public async Task> GetItems(int index, int count, string? orderingProperty = null,
+ bool descending = false)
+ {
+
+ return await this.GetUsers(index, count, this.ToEnum(orderingProperty), descending);
+
+ }
+
+ public async Task GetItemById(int id)
+ =>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == id));
+
+
+ public async Task AddItem(User user)
+ {
+ return await athletes.AddItem(user);
+ }
+
+ public async Task UpdateItem(int id, User user)
+ {
+ var oldUser = athletes.FirstOrDefault(s => s.Id == id);
+ if (oldUser == null) return null;
+ return await athletes.UpdateItem(oldUser, user);
+ }
+
+ public async Task DeleteItem(int id)
+ {
+ var user = athletes.FirstOrDefault(s => s.Id == id);
+ if (user == null) return false;
+ return await athletes.DeleteItem(user);
+ }
+
+ public async Task GetNbItems()
+ {
+ return await Task.FromResult(athletes.Count);
+ }
+}
\ No newline at end of file
diff --git a/src/StubAPI/Extensions.cs b/src/StubAPI/Extensions.cs
new file mode 100644
index 0000000..9917cac
--- /dev/null
+++ b/src/StubAPI/Extensions.cs
@@ -0,0 +1,70 @@
+namespace StubAPI;
+
+
+public static class Extensions
+{
+ internal static Task AddItem(this IList collection, T? item)
+ {
+ if(item == null || collection.Contains(item))
+ {
+ return Task.FromResult(default(T));
+ }
+ collection.Add(item);
+ return Task.FromResult(item);
+ }
+
+ internal static Task DeleteItem(this IList collection, T? item)
+ {
+ if(item == null)
+ {
+ return Task.FromResult(false);
+ }
+ bool result = collection.Remove(item!);
+ return Task.FromResult(result);
+ }
+
+ internal static Task UpdateItem(this IList collection, T? oldItem, T? newItem)
+ {
+ if(oldItem == null || newItem == null) return Task.FromResult(default(T));
+
+ if(!collection.Contains(oldItem))
+ {
+ return Task.FromResult(default(T));
+ }
+
+ collection.Remove(oldItem!);
+ collection.Add(newItem!);
+ return Task.FromResult(newItem);
+ }
+
+ public static IEnumerable GetItemsWithFilterAndOrdering(this IEnumerable list, Func filter, int index, int count, Enum? orderCriterium, bool descending = false ) where T : class
+ {
+ var filteredList = list.Where(filter);
+
+ if(orderCriterium != null)
+ {
+ filteredList = filteredList.OrderByCriteria(orderCriterium, descending);
+ }
+ return filteredList
+ .Skip(index * count)
+ .Take(count);
+ }
+
+ public static IOrderedEnumerable OrderByCriteria(this IEnumerable list, Enum orderCriterium, bool descending = false ) where T : class
+ {
+ var orderCriteriumString = orderCriterium.ToString();
+ if (orderCriteriumString.StartsWith("By"))
+ {
+ orderCriteriumString = orderCriteriumString.Substring(2);
+ }
+ var propertyInfo = typeof(T).GetProperty(orderCriteriumString);
+ if (propertyInfo == null)
+ {
+ throw new ArgumentException($"No property {orderCriterium} in type {typeof(T)}");
+ }
+
+ return descending ? list.OrderByDescending(x => propertyInfo.GetValue(x)) : list.OrderBy(x => propertyInfo.GetValue(x));
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/StubAPI/StubData.cs b/src/StubAPI/StubData.cs
new file mode 100644
index 0000000..1037cf6
--- /dev/null
+++ b/src/StubAPI/StubData.cs
@@ -0,0 +1,16 @@
+using Model.Manager;
+using Model.Repository;
+
+namespace StubAPI;
+
+public class StubData : IDataManager
+{
+ public IUserRepository UserRepository { get; }
+ public IActivityRepository ActivityRepository { get; }
+
+ public StubData()
+ {
+ UserRepository = new UserService();
+ ActivityRepository = new ActivityService();
+ }
+}
\ No newline at end of file
diff --git a/src/StubApi/AthleteStubDto.cs b/src/StubApi/AthleteStubDto.cs
new file mode 100644
index 0000000..48952e4
--- /dev/null
+++ b/src/StubApi/AthleteStubDto.cs
@@ -0,0 +1,4 @@
+using Shared;
+
+namespace
+
diff --git a/src/Tests/TestApi/GlobalUsings.cs b/src/Tests/TestApi/GlobalUsings.cs
new file mode 100644
index 0000000..ab67c7e
--- /dev/null
+++ b/src/Tests/TestApi/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
\ No newline at end of file
diff --git a/src/Tests/TestApi/TestApi.csproj b/src/Tests/TestApi/TestApi.csproj
new file mode 100644
index 0000000..719074b
--- /dev/null
+++ b/src/Tests/TestApi/TestApi.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Tests/TestApi/UserControllerTest.cs b/src/Tests/TestApi/UserControllerTest.cs
new file mode 100644
index 0000000..e69de29
diff --git a/src/Tests/TestConsoleApi/Class1.cs b/src/Tests/TestConsoleApi/Class1.cs
new file mode 100644
index 0000000..0b0d5dc
--- /dev/null
+++ b/src/Tests/TestConsoleApi/Class1.cs
@@ -0,0 +1,5 @@
+namespace TestConsoleApi;
+
+public class Class1
+{
+}
\ No newline at end of file
diff --git a/src/Tests/TestConsoleApi/TestConsoleApi.csproj b/src/Tests/TestConsoleApi/TestConsoleApi.csproj
new file mode 100644
index 0000000..3a63532
--- /dev/null
+++ b/src/Tests/TestConsoleApi/TestConsoleApi.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/src/Tests/TestsAPI/ClientTests/ClientTests.csproj b/src/Tests/TestsAPI/ClientTests/ClientTests.csproj
new file mode 100644
index 0000000..206b89a
--- /dev/null
+++ b/src/Tests/TestsAPI/ClientTests/ClientTests.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/src/Tests/TestsAPI/ClientTests/HttpClientManager.cs b/src/Tests/TestsAPI/ClientTests/HttpClientManager.cs
new file mode 100644
index 0000000..2e8e593
--- /dev/null
+++ b/src/Tests/TestsAPI/ClientTests/HttpClientManager.cs
@@ -0,0 +1,12 @@
+namespace ClientTests;
+
+public class HttpClientManager
+{
+ protected readonly HttpClient _httpClient;
+
+ public HttpClientManager(HttpClient httpClient)
+ {
+ _httpClient = httpClient;
+ _httpClient.BaseAddress = new Uri("https://localhost:7252");
+ }
+}
diff --git a/src/Tests/TestsAPI/ClientTests/Program.cs b/src/Tests/TestsAPI/ClientTests/Program.cs
new file mode 100644
index 0000000..83fa4f4
--- /dev/null
+++ b/src/Tests/TestsAPI/ClientTests/Program.cs
@@ -0,0 +1,2 @@
+// See https://aka.ms/new-console-template for more information
+Console.WriteLine("Hello, World!");
diff --git a/src/Tests/TestsAPI/TestsXUnit/GlobalUsings.cs b/src/Tests/TestsAPI/TestsXUnit/GlobalUsings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/src/Tests/TestsAPI/TestsXUnit/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/src/Tests/TestsAPI/TestsXUnit/TestsXUnit.csproj b/src/Tests/TestsAPI/TestsXUnit/TestsXUnit.csproj
new file mode 100644
index 0000000..22b0134
--- /dev/null
+++ b/src/Tests/TestsAPI/TestsXUnit/TestsXUnit.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
diff --git a/src/Tests/TestsAPI/TestsXUnit/UnitTest1.cs b/src/Tests/TestsAPI/TestsXUnit/UnitTest1.cs
new file mode 100644
index 0000000..70d745a
--- /dev/null
+++ b/src/Tests/TestsAPI/TestsXUnit/UnitTest1.cs
@@ -0,0 +1,10 @@
+namespace TestsXUnit;
+
+public class UnitTest1
+{
+ [Fact]
+ public void Test1()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/TestsAPI/UnitTestApi/GlobalUsings.cs b/src/Tests/TestsAPI/UnitTestApi/GlobalUsings.cs
new file mode 100644
index 0000000..ab67c7e
--- /dev/null
+++ b/src/Tests/TestsAPI/UnitTestApi/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
\ No newline at end of file
diff --git a/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj b/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj
new file mode 100644
index 0000000..45b5c8f
--- /dev/null
+++ b/src/Tests/TestsAPI/UnitTestApi/UnitTestApi.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Tests/TestsAPI/UnitTestApi/UserControllerTest.cs b/src/Tests/TestsAPI/UnitTestApi/UserControllerTest.cs
new file mode 100644
index 0000000..dd511e7
--- /dev/null
+++ b/src/Tests/TestsAPI/UnitTestApi/UserControllerTest.cs
@@ -0,0 +1,124 @@
+using Dto;
+using HeartTrackAPI.Controllers;
+using HeartTrackAPI.Request;
+using HeartTrackAPI.Responce;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging.Abstractions;
+using Model.Manager;
+using Model.Repository;
+using StubAPI;
+
+namespace UnitTestApi;
+
+[TestClass]
+public class UserControllerTest
+{
+ private readonly IDataManager StubDataManager;
+ private readonly UsersController _usersController;
+
+ public UserControllerTest()
+ {
+ StubDataManager = new StubData();
+ _usersController = new UsersController(new NullLogger(), StubDataManager);
+
+ }
+
+ [TestMethod]
+ public void Get_ReturnsPageResponse_WhenRequestIsValid()
+ {
+ // Arrange
+ var request = new PageRequest
+ {
+ Index = 0,
+ Count = 10,
+ OrderingPropertyName = "Id",
+ Descending = false
+ };
+
+ // Act
+ //var result = _usersController.Get(request).Result as OkObjectResult;
+
+ // Assert
+ // Assert.IsNotNull(result);
+ //Assert.IsInstanceOfType(result.Value, typeof(PageResponse));
+ }
+ /*
+ [TestMethod]
+ public void GetById_ReturnsUserDto_WhenRequestIsValid()
+ {
+ // Arrange
+ var id = 1;
+
+ // Act
+ var result = _usersController.GetById(id).Result as OkObjectResult;
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOfType(result.Value, typeof(UserDto));
+ }
+
+ [TestMethod]
+ public void GetById_Returns404_WhenIdIsInvalid()
+ {
+ // Arrange
+ var id = 0;
+
+ // Act
+ var result = _usersController.GetById(id).Result as NotFoundResult;
+
+ // Assert
+ Assert.IsNotNull(result);
+ }
+
+ [TestMethod]
+ public void GetById_Returns500_WheExceptionIsThrown()
+ {
+ // Arrange
+ var id = 0;
+
+ // Act
+ var result = _usersController.GetById(id).Result as StatusCodeResult;
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(500, result.StatusCode);
+ }
+
+ [TestMethod]
+ public void Count_ReturnsInt_WhenRequestIsValid()
+ {
+ // Act
+ var result = _usersController.Count().Result as OkObjectResult;
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.IsInstanceOfType(result.Value, typeof(int));
+ }
+
+ [TestMethod]
+ public void Count_Returns500_WheExceptionIsThrown()
+ {
+ // Act
+ var result = _usersController.Count().Result as StatusCodeResult;
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(500, result.StatusCode);
+ }
+
+ [TestMethod]
+ public void Update_ReturnsUserDto_WhenRequestIsValid()
+ {
+ // Arrange
+ var id = 1;
+ var user = new UserDto
+ {
+ Id = 1,
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "toto@eoeo.fr",
+ };
+
+ }*/
+
+}
\ No newline at end of file