diff --git a/src/Dto/ActivityDto.cs b/src/Dto/ActivityDto.cs index e69de29..d4bf5f1 100644 --- a/src/Dto/ActivityDto.cs +++ 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/HeartTrackAPI/Controllers/ActivityController.cs b/src/HeartTrackAPI/Controllers/ActivityController.cs new file mode 100644 index 0000000..7c9d2b0 --- /dev/null +++ b/src/HeartTrackAPI/Controllers/ActivityController.cs @@ -0,0 +1,98 @@ +using Dto; +using HeartTrackAPI.Request; +using HeartTrackAPI.Responce; +using Microsoft.AspNetCore.Mvc; +using Shared; +using Model; +/* +namespace HeartTrackAPI.Controllers; +[ApiController] +[Route("api/activities")] +public class ActivityController : Controller +{ + private readonly IActivityService _activityService; + private readonly ILogger _logger; + + public ActivityController(IActivityService 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); + // request.OrderingPropertyName + 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 index 8887667..ceb09a4 100644 --- a/src/HeartTrackAPI/Controllers/UsersController.cs +++ b/src/HeartTrackAPI/Controllers/UsersController.cs @@ -12,7 +12,6 @@ namespace HeartTrackAPI.Controllers; [Route("api/users")] public class UsersController : Controller { - // For the moment only support user who are athletes next handle user that are coach or athlete private readonly ILogger _logger; private readonly IUserService _userService; public UsersController(ILogger logger, IUserService usersService) @@ -22,7 +21,7 @@ public class UsersController : Controller } [HttpGet] - [ProducesResponseType(typeof(IEnumerable), 200)] + [ProducesResponseType(typeof(PageResponse), 200)] [ProducesResponseType(400)] [ProducesResponseType(500)] public async Task>> GetAllAthletes([FromQuery] PageRequest request) @@ -35,9 +34,10 @@ public class UsersController : Controller _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(GetAllAthletes), null); - // request.OrderingPropertyName - var athletes = await _userService.GetUsers(request.Index, request.Count, AthleteOrderCriteria.None, request.Descending ?? false); + + 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); } @@ -95,7 +95,6 @@ public class UsersController : Controller [ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] - // need to adapt with coach public async Task> UpdateUser(int id, [FromBody] UserDto user) { try diff --git a/src/HeartTrackAPI/HeartTrackAPI.csproj b/src/HeartTrackAPI/HeartTrackAPI.csproj index 35ec313..f24f646 100644 --- a/src/HeartTrackAPI/HeartTrackAPI.csproj +++ b/src/HeartTrackAPI/HeartTrackAPI.csproj @@ -20,4 +20,10 @@ + + + ..\..\..\..\..\.nuget\packages\newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll + + + diff --git a/src/HeartTrackAPI/Request/PageRequest.cs b/src/HeartTrackAPI/Request/PageRequest.cs index fce6be3..bc04934 100644 --- a/src/HeartTrackAPI/Request/PageRequest.cs +++ b/src/HeartTrackAPI/Request/PageRequest.cs @@ -1,9 +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; -} \ No newline at end of file +} diff --git a/src/Model/Activity.cs b/src/Model/Activity.cs new file mode 100644 index 0000000..4e5cf64 --- /dev/null +++ b/src/Model/Activity.cs @@ -0,0 +1,68 @@ +using System.ComponentModel.DataAnnotations; + +namespace Model; +public class Activity + { + public int IdActivity { get; private 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) + { + IdActivity = 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 #{IdActivity}: {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/IActivityService.cs b/src/Model/IActivityService.cs new file mode 100644 index 0000000..152b1c3 --- /dev/null +++ b/src/Model/IActivityService.cs @@ -0,0 +1,13 @@ +using Shared; + +namespace Model; + +public interface IActivityService +{ + 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/IAthleteService.cs b/src/Model/IAthleteService.cs index ec9fac8..a89ddc2 100644 --- a/src/Model/IAthleteService.cs +++ b/src/Model/IAthleteService.cs @@ -12,5 +12,4 @@ public interface IUserService public Task UpdateUser(int id, User user); public Task DeleteUser(int id); public Task GetNbItems(); - } \ 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 index 681b04e..4bc54f0 100644 --- a/src/Shared/AthleteOrderCriteria.cs +++ b/src/Shared/AthleteOrderCriteria.cs @@ -16,3 +16,30 @@ } + +/*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/StubAPI/AthleteService.cs b/src/StubAPI/AthleteService.cs index 9591e51..bd2f9db 100644 --- a/src/StubAPI/AthleteService.cs +++ b/src/StubAPI/AthleteService.cs @@ -9,21 +9,21 @@ public class UserService : IUserService [ new User { - Id = 1, Username = "Athlete1", ProfilePicture = "default.png", FirstName = "First1", LastName = "Last1", + 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 = "athlete1@example.com", Role = new Athlete() + Email = "john.doe@example.com", Role = new Athlete() }, new User { - Id = 2, Username = "Athlete2", ProfilePicture = "default.png", FirstName = "First2", LastName = "Last2", + 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 = "default.png", FirstName = "First3", LastName = "Last3", + 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() } @@ -32,9 +32,7 @@ public class UserService : IUserService public async Task> GetUsers(int index, int count, AthleteOrderCriteria criteria, bool descending = false) - { - throw new NotImplementedException(); - } + => athletes.GetItemsWithFilterAndOrdering(c=>true,index, count, criteria != AthleteOrderCriteria.None ? criteria: null , descending); public async Task GetUserByIdAsync(int id) { diff --git a/src/StubAPI/Extensions.cs b/src/StubAPI/Extensions.cs index e7a036c..9917cac 100644 --- a/src/StubAPI/Extensions.cs +++ b/src/StubAPI/Extensions.cs @@ -36,5 +36,35 @@ public static class Extensions 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