Compare commits
122 Commits
@ -1,91 +0,0 @@
|
||||
[retour au README.md](../../../README.md)
|
||||
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
|
||||
|
||||
# Diagramme de Classes : Gestion des Utilisateurs et Notifications
|
||||
|
||||
Bienvenue dans le cœur de notre système, où la gestion des utilisateurs et des notifications prend vie à travers ce diagramme de classes. Explorez les relations et les fonctionnalités essentielles qui orchestrent l'interaction entre les utilisateurs, les demandes d'amis, et les notifications.
|
||||
|
||||
**Entités Principales :**
|
||||
|
||||
- **Utilisateur (User) :** Représente les individus inscrits sur notre plateforme, caractérisés par leur nom et établissant des liens d'amitié avec d'autres utilisateurs.
|
||||
|
||||
- **Notification (Notification) :** Contient le texte informatif des notifications qui peuvent être émises par le système.
|
||||
|
||||
- **Demande d'Ami (Ask) :** Modélise une demande d'amitié émise par un utilisateur en direction d'un autre.
|
||||
|
||||
**Interfaces et Classes Abstraites :**
|
||||
|
||||
- **INotifier :** Interface définissant la méthode `notify()`, implémentée par des classes concrètes pour gérer la notification aux observateurs.
|
||||
|
||||
- **Observer :** Interface définissant la méthode `update()`, implémentée par les classes qui souhaitent être informées des changements dans un sujet observé.
|
||||
|
||||
- **UserManager :** Classe abstraite gérant la logique métier liée aux utilisateurs, tels que l'ajout ou la suppression d'amis, la réponse aux demandes d'amis, et la récupération de la liste d'amis.
|
||||
|
||||
- **IUserRepository :** Interface définissant les méthodes pour la recherche d'utilisateurs et l'ajout d'un nouvel utilisateur.
|
||||
|
||||
**Relations Clés :**
|
||||
|
||||
- Les utilisateurs peuvent avoir plusieurs amis et plusieurs notifications.
|
||||
|
||||
- La classe UserManager est connectée à IUserRepository pour gérer les opérations liées aux utilisateurs.
|
||||
|
||||
- Observer et Subject sont des composants du modèle de conception "Observer", permettant la notification efficace des changements dans le système.
|
||||
|
||||
Plongez-vous dans ce diagramme pour découvrir comment notre application crée un écosystème social dynamique, permettant aux utilisateurs d'interagir, de rester informés et de développer des liens significatifs au sein de la communauté.
|
||||
|
||||
|
||||
```plantuml
|
||||
class User {
|
||||
+ name : string
|
||||
}
|
||||
|
||||
User "1" --> "*" User: friends
|
||||
User "1" --> "*" Notification: notifications
|
||||
User "1" --> "*" Ask: friendRequests
|
||||
class Notification {
|
||||
- text : string
|
||||
}
|
||||
|
||||
interface INotifier {
|
||||
+ notify() : void
|
||||
}
|
||||
|
||||
INotifier --|> Observer
|
||||
|
||||
abstract class UserManager {
|
||||
- currentUser : User
|
||||
+ deleteFriend(userId : int) : void
|
||||
+ addFriend(userId : int) : void
|
||||
+ respondToFriendRequest(requestId : int, choice : bool) : void
|
||||
+ getFriends(userId : int) : User[]
|
||||
}
|
||||
|
||||
class Ask {
|
||||
- fromUser : int
|
||||
- toUser : int
|
||||
}
|
||||
|
||||
Ask --|> Subject
|
||||
|
||||
abstract class Subject {
|
||||
+ attach(o : Observer) : void
|
||||
+ detach(o : Observer) : void
|
||||
+ notify() : void
|
||||
}
|
||||
|
||||
Subject "1" --> "*" Observer
|
||||
interface Observer {
|
||||
+ update() : void
|
||||
}
|
||||
|
||||
UserManager ..> User
|
||||
UserManager o-- IUserRepository
|
||||
UserManager o-- INotifier
|
||||
|
||||
interface IUserRepository {
|
||||
+ findByUsername(username : string) : User
|
||||
+ addUser(user : User) : bool
|
||||
}
|
||||
|
||||
IUserRepository ..> User
|
||||
```
|
@ -1,200 +0,0 @@
|
||||
[retour au README.md](../../../README.md)
|
||||
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
|
||||
|
||||
# Introduction au Modèle de Données de l'Application
|
||||
|
||||
L'architecture de données de notre application de suivi d'activités sportives repose sur un modèle robuste, avec des entités clés pour représenter les activités, les athlètes et les coachs. Découvrez les composants principaux de notre modèle de données :
|
||||
|
||||
## Activité
|
||||
L'entité Activité représente une session d'activité sportive avec des détails variés tels que le type d'activité, la date, la durée, l'effort ressenti, etc. Le `ActiviteEntity` encapsule ces données, tandis que le `ActiviteGateway` gère la communication avec la base de données pour les activités.
|
||||
|
||||
## Athlète
|
||||
L'entité Athlète représente un utilisateur de l'application qui participe à des activités sportives. Le `AthleteEntity` stocke les détails de l'athlète, et le `AtheletGateway` facilite l'accès et la gestion des données des athlètes.
|
||||
|
||||
## Coach
|
||||
L'entité Coach représente un utilisateur qui peut superviser et coacher d'autres athlètes. Le `CoachEntity` stocke les détails du coach, tandis que le `CoachGateway` gère les interactions avec la base de données.
|
||||
|
||||
## Mapper
|
||||
Les mappers, tels que `ActiviteMapper`, `AthleteMapper`, et `CoachMapper`, facilitent la conversion entre les entités et les modèles utilisés dans l'application.
|
||||
|
||||
## Connexion à la Base de Données
|
||||
La classe `Connection` étend de `PDO` et assure la connexion à la base de données. Chaque Gateway utilise cette connexion pour interagir avec la base de données.
|
||||
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
class ActiviteEntity {
|
||||
- idActivite: int
|
||||
- type: string
|
||||
- date: string
|
||||
- heureDebut: string
|
||||
- heureFin: string
|
||||
- effortRessenti: int
|
||||
- variabilite: int
|
||||
- variance: int
|
||||
- ecartType: int
|
||||
- moyenne: int
|
||||
- maximum: int
|
||||
- minimum: int
|
||||
- temperatureMoyenne: int
|
||||
+ getIdActivite(): int
|
||||
+ getType(): string
|
||||
+ getDate(): string
|
||||
+ getHeureDebut(): string
|
||||
+ getHeureFin(): string
|
||||
+ getEffortRessenti(): int
|
||||
+ getVariabilite(): int
|
||||
+ getVariance(): int
|
||||
+ getEcartType(): int
|
||||
+ getMoyenne(): int
|
||||
+ getMaximum(): int
|
||||
+ getMinimum(): int
|
||||
+ getTemperatureMoyenne(): int
|
||||
+ setIdActivite(idActivite: int): void
|
||||
+ setType(type: string): void
|
||||
+ setDate(date: string): void
|
||||
+ setHeureDebut(heureDebut: string): void
|
||||
+ setHeureFin(heureFin: string): void
|
||||
+ setEffortRessenti(effortRessenti: int): void
|
||||
+ setVariabilite(variabilite: int): void
|
||||
+ setVariance(variance: int): void
|
||||
+ setEcartType(ecartType: int): void
|
||||
+ setMoyenne(moyenne: int): void
|
||||
+ setMaximum(maximum: int): void
|
||||
+ setMinimum(minimum: int): void
|
||||
+ setTemperatureMoyenne(temperatureMoyenne: int): void
|
||||
}
|
||||
class ActiviteGateway {
|
||||
+ __construct(connection: Connection)
|
||||
+ getActivite(): ?array
|
||||
+ getActiviteById(activiteId: int): ?array
|
||||
+ getActiviteByType(type: string): ?array
|
||||
+ getActiviteByDate(date: string): ?array
|
||||
+ getActiviteByTimeRange(startTime: string, endTime: string): ?array
|
||||
+ getActiviteByEffort(effortRessenti: int): ?array
|
||||
+ getActiviteByVariability(variabilite: int): ?array
|
||||
+ getActiviteByTemperature(temperatureMoyenne: int): ?array
|
||||
+ addActivite(activite: ActiviteEntity): bool
|
||||
+ updateActivite(oldActivite: ActiviteEntity, newActivite: ActiviteEntity): bool
|
||||
+ deleteActivite(idActivite: int): bool
|
||||
}
|
||||
class ActiviteMapper {
|
||||
+ map(data: array): ActiviteEntity
|
||||
+ ActiviteEntityToModel(activiteEntity: ActiviteEntity): Activite
|
||||
}
|
||||
class AthleteEntity {
|
||||
- idAthlete: int
|
||||
- nom: string
|
||||
- prenom: string
|
||||
- email: string
|
||||
- sexe: string
|
||||
- taille: float
|
||||
- poids: float
|
||||
- motDePasse: string
|
||||
- dateNaissance: string
|
||||
+ getIdAthlete(): int
|
||||
+ getNom(): string
|
||||
+ getPrenom(): string
|
||||
+ getEmail(): string
|
||||
+ getSexe(): string
|
||||
+ getTaille(): float
|
||||
+ getPoids(): float
|
||||
+ getMotDePasse(): string
|
||||
+ getDateNaissance(): string
|
||||
+ setIdAthlete(idAthlete: int): void
|
||||
+ setNom(nom: string): void
|
||||
+ setPrenom(prenom: string): void
|
||||
+ setEmail(email: string): void
|
||||
+ setSexe(sexe: string): void
|
||||
+ setTaille(taille: float): void
|
||||
+ setPoids(poids: float): void
|
||||
+ setMotDePasse(motDePasse: string): void
|
||||
+ setDateNaissance(dateNaissance: string): void
|
||||
}
|
||||
class AtheletGateway {
|
||||
+ __construct(connection: Connection)
|
||||
+ getAthlete(): ?array
|
||||
+ getAthleteById(userId: int): ?array
|
||||
+ getAthleteByName(name: string): ?array
|
||||
+ getAthleteByFirstName(firstName: string): ?array
|
||||
+ getAthleteByEmail(email: string): ?array
|
||||
+ getAthleteByGender(gender: string): ?array
|
||||
+ getAthleteByHeight(height: int): ?array
|
||||
+ getAthleteByWeight(weight: int): ?array
|
||||
+ getAthleteByBirthDate(birthdate: string): ?array
|
||||
+ addAthlete(athlete: AthleteEntity): bool
|
||||
+ updateAthlete(oldAthlete: AthleteEntity, newAthlete: AthleteEntity): bool
|
||||
+ deleteAthlete(idAthlete: int): bool
|
||||
}
|
||||
class AthleteMapper {
|
||||
+ fromSqlToEntity(data: array): array
|
||||
+ athleteEntityToModel(athleteEntity: AthleteEntity): User
|
||||
+ athleteToEntity(user: User): AthleteEntity
|
||||
}
|
||||
class CoachEntity {
|
||||
- idCoach: int
|
||||
- nom: string
|
||||
- prenom: string
|
||||
- email: string
|
||||
- sexe: string
|
||||
- taille: float
|
||||
- poids: float
|
||||
- motDePasse: string
|
||||
- dateNaissance: string
|
||||
+ getIdCoach(): int
|
||||
+ getNom(): string
|
||||
+ getPrenom(): string
|
||||
+ getEmail(): string
|
||||
+ getSexe(): string
|
||||
+ getTaille(): float
|
||||
+ getPoids(): float
|
||||
+ getMotDePasse(): string
|
||||
+ getDateNaissance(): string
|
||||
+ setIdCoach(idCoach: int): void
|
||||
+ setNom(nom: string): void
|
||||
+ setPrenom(prenom: string): void
|
||||
+ setEmail(email: string): void
|
||||
+ setSexe(sexe: string): void
|
||||
+ setTaille(taille: float): void
|
||||
+ setPoids(poids: float): void
|
||||
+ setMotDePasse(motDePasse: string): void
|
||||
+ setDateNaissance(dateNaissance: string): void
|
||||
}
|
||||
class CoachGateway {
|
||||
+ __construct(connection: Connection)
|
||||
+ getCoach(): ?array
|
||||
+ getCoachById(userId: int): ?array
|
||||
+ getCoachByName(name: string): ?array
|
||||
+ getCoachByFirstName(firstName: string): ?array
|
||||
+ getCoachByEmail(email: string): ?array
|
||||
+ getCoachByGender(gender : string): ?array
|
||||
+ getCoachByHeight(height: int): ?array
|
||||
+ getCoachByBirthDate(birthdate: string): ?array
|
||||
+ addCoach(coach: CoachEntity): bool
|
||||
+ updateCoach(oldCoach: CoachEntity, newCoach: CoachEntity): bool
|
||||
+ deleteCoach(idCoach: int): bool
|
||||
}
|
||||
class CoachMapper {
|
||||
+ map(data: array): CoachEntity
|
||||
+ CoachEntityToModel(coachEntity: CoachEntity): User
|
||||
+ CoachToEntity(user: User): CoachEntity
|
||||
}
|
||||
class Connection extends PDO {
|
||||
- stmt
|
||||
+ __construct(dsn: string, username: string, password: string)
|
||||
+ executeQuery(query: string, parameters: array): bool
|
||||
+ executeWithErrorHandling(query: string, params: array): array
|
||||
+ getResults(): array
|
||||
}
|
||||
|
||||
Connection <- ActiviteGateway : connection
|
||||
Connection <- AtheletGateway : connection
|
||||
Connection <- CoachGateway : connection
|
||||
AthleteMapper -> AthleteEntity
|
||||
CoachMapper -> CoachEntity
|
||||
ActiviteMapper -> ActiviteEntity
|
||||
ActiviteMapper -> ActiviteGateway
|
||||
CoachMapper -> CoachGateway
|
||||
AthleteMapper -> AtheletGateway
|
||||
@enduml
|
||||
```
|
@ -1,136 +0,0 @@
|
||||
[retour au README.md](../../../README.md)
|
||||
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
|
||||
|
||||
# Diagramme de classes pour l'importation de fichiers .fit
|
||||
|
||||
Bienvenue dans le monde de la gestion d'activités sportives avec notre application innovante ! Cette user story se concentre sur une fonctionnalité essentielle qui améliorera l'expérience des utilisateurs : l'importation de fichiers .fit. Nous avons conçu un diagramme de classes pour vous offrir une vision claire et structurée de la manière dont cette fonctionnalité est implémentée au sein de notre application.
|
||||
|
||||
**Acteurs Principaux :**
|
||||
|
||||
- Utilisateur (User) : Représente un individu inscrit sur notre plateforme, avec la capacité d'importer des fichiers .fit.
|
||||
- Athlète (Athlete) : Un type spécialisé d'utilisateur, bénéficiant de fonctionnalités supplémentaires liées à la gestion d'activités sportives.
|
||||
|
||||
**Entités Clés :**
|
||||
|
||||
- Activité (Activity) : Représente une session d'activité physique, avec des détails tels que le type, la date, la durée, et plus encore.
|
||||
- Gestionnaires (Managers) : Gérant différentes facettes de l'application, notamment les utilisateurs, les activités et les fichiers.
|
||||
|
||||
**Fonctionnalité Clé :**
|
||||
|
||||
- Importation de fichiers .fit : Permet aux utilisateurs de charger des données provenant de fichiers .fit, générés par des dispositifs de suivi d'activité. Ces fichiers contiennent des informations précieuses telles que la fréquence cardiaque, la distance parcourue et d'autres métriques essentielles.
|
||||
|
||||
**Architecture :**
|
||||
|
||||
- AuthService (Service d'Authentification) : Gère l'authentification des utilisateurs, garantissant un accès sécurisé à la fonction d'importation.
|
||||
- UserManager (Gestionnaire d'Utilisateurs) : Gère les opérations liées aux utilisateurs, y compris l'importation de fichiers .fit.
|
||||
ActivityManager (Gestionnaire d'Activités) : Responsable du stockage et de la gestion des activités importées.
|
||||
|
||||
**Objectif :**
|
||||
|
||||
Offrir aux utilisateurs, en particulier aux athlètes, la possibilité d'enrichir leur profil et de suivre leur performance en important des données détaillées à partir de fichiers .fit.
|
||||
|
||||
|
||||
```plantuml
|
||||
@startuml issue028_DiagrammeDeClasses
|
||||
class Activite {
|
||||
-idActivite:int
|
||||
-type:String
|
||||
-date:Date
|
||||
-heureDebut:Date
|
||||
-heureFin:Date
|
||||
-effortRessenti:int
|
||||
-variability:float
|
||||
-variance:float
|
||||
-standardDeviation:float
|
||||
-average:float
|
||||
-maximum:int
|
||||
-minimum:int
|
||||
-avrTemperature:float
|
||||
-hasAutoPause:boolean
|
||||
+getIdActivite():int
|
||||
+getType():String
|
||||
+getDate():Date
|
||||
+getHeureDebut():Date
|
||||
+getHeureFin():Date
|
||||
+getEffortRessenti():int
|
||||
+getVariability():float
|
||||
+getVariance():float
|
||||
+getStandardDeviation():float
|
||||
+getAverage():float
|
||||
+getMaximum():int
|
||||
+getMinimum():int
|
||||
+getAvrTemperature():float
|
||||
+setType(type:String):void
|
||||
+setEffortRessenti(effortRessenti:int):void
|
||||
+__toString():String
|
||||
}
|
||||
class Role {
|
||||
-id:int
|
||||
}
|
||||
class Athlete {
|
||||
+getActivities():array
|
||||
+addActivity(myActivity:Activity):boolean
|
||||
}
|
||||
class User {
|
||||
-id:int
|
||||
-username:String
|
||||
-nom:String
|
||||
-prenom:String
|
||||
-email:String
|
||||
-motDePasse:String
|
||||
-sexe:String
|
||||
-taille:float
|
||||
-poids:float
|
||||
-dateNaissance:Date
|
||||
+getId():int
|
||||
+setId(id:int):void
|
||||
+getUsername():String
|
||||
+setUsername(username:String):void
|
||||
+getNom():String
|
||||
+setNom(nom:String):void
|
||||
+getPrenom():String
|
||||
+setPrenom(prenom:String):void
|
||||
+getEmail():String
|
||||
+setEmail(email:String):void
|
||||
+getMotDePasse():String
|
||||
+setMotDePasse(motDePasse:String):void
|
||||
+getSexe():String
|
||||
+setSexe(sexe:String):void
|
||||
+getTaille():float
|
||||
+setTaille(taille:float):void
|
||||
+getPoids():float
|
||||
+setPoids(poids:float):void
|
||||
+getDateNaissance():Date
|
||||
+setDateNaissance(dateNaissance:Date):void
|
||||
+getRole():Role
|
||||
+setRole(role:Role):void
|
||||
+isValidPassword(password:String):boolean
|
||||
+__toString():String
|
||||
}
|
||||
class AthleteManager {
|
||||
+getActivities():array
|
||||
}
|
||||
class ActivityManager {
|
||||
+saveFitFileToJSON(monFichierFit:object):boolean
|
||||
+uploadFile(type:string, effortRessenti:int, file_path_or_data:string|resource, options:array):boolean
|
||||
}
|
||||
class DataManager {
|
||||
}
|
||||
class UserManager {
|
||||
+login(loginUser:string, passwordUser:string):boolean
|
||||
+register(loginUser:string, passwordUser:string, data:array):boolean
|
||||
+deconnecter():boolean
|
||||
}
|
||||
|
||||
User -> Role: role
|
||||
Athlete -|> Role
|
||||
DataManager -> UserManager: -userMgr
|
||||
DataManager -> AthleteManager: -athleteMgr
|
||||
DataManager -> ActivityManager: -activityMgr
|
||||
UserManager -> AuthService: -authService
|
||||
UserManager -> User: -currentUser
|
||||
ActivityManager -> AuthService: -authService
|
||||
Athlete -> Activite: listActivite
|
||||
AthleteManager -> AuthService: -authService
|
||||
@enduml
|
||||
```
|
@ -1,40 +0,0 @@
|
||||
[retour au README.md](../../../README.md)
|
||||
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
|
||||
|
||||
# Introduction au Processus de Connexion sur la Plateforme
|
||||
|
||||
Bienvenue sur notre plateforme de gestion d'activités sportives ! Pour offrir une expérience fluide et sécurisée, nous avons mis en place un processus de connexion intuitif. Découvrez comment accéder à votre compte ou créer un nouveau compte en quelques étapes simples.
|
||||
|
||||
**Étapes du Processus :**
|
||||
|
||||
1. **Demande de Page de Connexion :** L'utilisateur démarre en exprimant le désir de se connecter à la plateforme.
|
||||
|
||||
2. **Vérification de la Connexion Préexistante :** Le système vérifie si l'utilisateur est déjà connecté. En cas de connexion active, l'utilisateur est redirigé directement vers sa page de compte.
|
||||
|
||||
3. **Page de Connexion :** Si l'utilisateur n'est pas encore connecté, il est dirigé vers la page de connexion, où il peut saisir ses informations d'identification.
|
||||
|
||||
4. **Choix pour les Utilisateurs Possédant un Compte :** Si l'utilisateur a déjà un compte, il peut fournir ses informations de connexion existantes.
|
||||
|
||||
5. **Création de Compte pour les Nouveaux Utilisateurs :** Pour ceux qui n'ont pas encore de compte, l'option de création de compte est disponible. L'utilisateur peut fournir les détails nécessaires pour créer son compte.
|
||||
|
||||
6. **Page de Création de Compte :** Une page dédiée guide l'utilisateur tout au long du processus de création de compte, lui permettant de saisir les informations nécessaires.
|
||||
|
||||
7. **Validation et Connexion :** Une fois que les informations de connexion ou de création de compte sont fournies, le système procède à la vérification et connecte l'utilisateur à son compte.
|
||||
|
||||
|
||||
```plantuml
|
||||
actor User as u
|
||||
u->Systeme : demandePageConnexion()
|
||||
alt User déjà connecté
|
||||
Systeme-->u : redirectionPageCompte()
|
||||
end
|
||||
Systeme-->u : PageConnexion()
|
||||
alt User possède déjà un compte
|
||||
u->Systeme:InfosConnexion()
|
||||
else
|
||||
u->Systeme:CreerCompte()
|
||||
Systeme-->u :PageCreationCompte()
|
||||
u->Systeme:InfosCreationCompte()
|
||||
end
|
||||
Systeme-->u :Connecter()
|
||||
```
|
@ -1,25 +0,0 @@
|
||||
using Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DbContextLib.Identity;
|
||||
|
||||
public class AuthDbContext: IdentityDbContext<IdentityUser>
|
||||
{
|
||||
|
||||
public AuthDbContext(DbContextOptions<AuthDbContext> options) : base(options) { }
|
||||
public AuthDbContext() { }
|
||||
/*
|
||||
/// <summary>
|
||||
/// Configures the database options if they are not already configured.
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder">The options builder instance.</param>
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (!optionsBuilder.IsConfigured)
|
||||
{
|
||||
optionsBuilder.UseSqlite($"Data Source=uca.HeartTrack.db");
|
||||
}
|
||||
}*/
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Dto.Auth;
|
||||
|
||||
public class LoginRequestDto
|
||||
{
|
||||
[Required(ErrorMessage = "Username is required")]
|
||||
public string Username { get; set; }
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
public string Password { get; set; }
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
namespace Dto.Auth;
|
||||
|
||||
public class AuthResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the access token issued by the OAuth provider.
|
||||
/// </summary>
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the token type.</summary>
|
||||
/// <remarks>Typically the string “bearer”.</remarks>
|
||||
public string? TokenType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a refresh token that applications can use to obtain another access token if tokens can expire.
|
||||
/// </summary>
|
||||
public string? RefreshToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the validatity lifetime of the token in seconds.
|
||||
/// </summary>
|
||||
public string? ExpiresIn { get; set; }
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Dto.Auth;
|
||||
|
||||
public class RegisterRequestDto
|
||||
{
|
||||
|
||||
[MaxLength(100)]
|
||||
[Required(ErrorMessage = "Username is required")]
|
||||
public string Username { get; set; }
|
||||
[MaxLength(150)]
|
||||
[Required(ErrorMessage = "LastName is required")]
|
||||
public string LastName { get; set; }
|
||||
[MaxLength(100)]
|
||||
[Required(ErrorMessage = "FirstName is required")]
|
||||
public string FirstName { get; set; }
|
||||
[Required(ErrorMessage = "Email is required")]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
[Required(ErrorMessage = "Sexe is required")]
|
||||
public char Sexe { get; set; }
|
||||
[Required(ErrorMessage = "Size is required")]
|
||||
public float Size { get; set; }
|
||||
[Required(ErrorMessage = "Weight is required")]
|
||||
public float Weight { get; set; }
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
public string Password { get; set; }
|
||||
[Required(ErrorMessage = "DateOfBirth is required")]
|
||||
public DateTime DateOfBirth { get; set; }
|
||||
public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
|
||||
[Required(ErrorMessage = "Role is required")]
|
||||
public bool IsCoach { get; set; }
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using Dto.Tiny;
|
||||
|
||||
namespace Dto;
|
||||
|
||||
public class NewActivityDto
|
||||
{
|
||||
public ActivityTinyDto Activity { get; set; }
|
||||
public HeartRateTinyDto[]? HeartRates { get; set; }
|
||||
public int? DataSourceId { get; set; }
|
||||
public int AthleteId { get; set; }
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using Dto.Tiny;
|
||||
|
||||
namespace Dto;
|
||||
|
||||
public class ResponseActivityDto
|
||||
{
|
||||
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; }
|
||||
public int EffortFelt { get; set; }
|
||||
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 HeartRateTinyDto[]? HeartRates { get; set; }
|
||||
public DataSourceTinyDto? DataSource { get; set; }
|
||||
public UserTinyDto? Athlete { get; set; }
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using Dto.Tiny;
|
||||
|
||||
namespace Dto;
|
||||
|
||||
public class ResponseDataSourceDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Type { get; set; } = "Unknown";
|
||||
|
||||
public string Model { get; set; }
|
||||
|
||||
public float Precision { get; set; }
|
||||
|
||||
public ActivityTinyDto[]? Activities { get; set; }
|
||||
|
||||
public UserTinyDto[]? Users { get; set; }
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Dto.Tiny;
|
||||
|
||||
namespace Dto;
|
||||
|
||||
public class ResponseUserDto
|
||||
{
|
||||
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 char 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; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
|
||||
public bool IsCoach { get; set; }
|
||||
public LargeImageDto? Image { get; set; }
|
||||
public ActivityTinyDto[] Activities { get; set; }
|
||||
public DataSourceTinyDto? DataSource { get; set; }
|
||||
public FriendshipDto?[] Followers { get; set; }
|
||||
public FriendshipDto?[] Followings { get; set; }
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
namespace Dto.Tiny;
|
||||
|
||||
public class ActivityTinyDto
|
||||
{
|
||||
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; }
|
||||
public int EffortFelt { get; set; }
|
||||
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; }
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Dto.Tiny;
|
||||
|
||||
public class DataSourceTinyDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Type { get; set; } = "Unknown";
|
||||
|
||||
public string Model { get; set; }
|
||||
|
||||
public float Precision { get; set; }
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Dto.Tiny;
|
||||
|
||||
public class FriendshipDto
|
||||
{
|
||||
public int FollowedId { get; set; }
|
||||
public int FollowerId { get; set; }
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
namespace Dto.Tiny;
|
||||
|
||||
public class HeartRateTinyDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public double? Latitude { get; set; }
|
||||
public double? Longitude { get; set; }
|
||||
public double? Altitude { get; set; }
|
||||
public int HeartRate { get; set; }
|
||||
public int? Cadence { get; set; }
|
||||
public double? Distance { get; set; }
|
||||
public double? Speed { get; set; }
|
||||
public int? Power { get; set; }
|
||||
public double? Temperature { get; set; }
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Dto.Tiny;
|
||||
|
||||
public class UserTinyDto
|
||||
{
|
||||
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 char Sexe { get; set; }
|
||||
public float Length { get; set; }
|
||||
public float Weight { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public DateTime DateOfBirth { get; set; }
|
||||
public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
|
||||
public bool IsCoach { get; set; }
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
using Dto;
|
||||
using Dto.Tiny;
|
||||
using Entities;
|
||||
using Shared;
|
||||
|
||||
namespace Entities2Dto;
|
||||
|
||||
public static class DataSourceMapper
|
||||
{
|
||||
private static GenericMapper<DataSourceTinyDto, DataSourceEntity> _mapper = new();
|
||||
|
||||
private static GenericMapper<ResponseDataSourceDto, DataSourceEntity> _mapperFull = new();
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_mapper.Reset();
|
||||
_mapperFull.Reset();
|
||||
}
|
||||
|
||||
public static DataSourceTinyDto ToTinyDto(this DataSourceEntity entity)
|
||||
{
|
||||
Func<DataSourceEntity, DataSourceTinyDto> create = dataSourceEntity => new DataSourceTinyDto
|
||||
{
|
||||
Id = dataSourceEntity.IdSource,
|
||||
Type = dataSourceEntity.Type,
|
||||
Model = dataSourceEntity.Model,
|
||||
Precision = dataSourceEntity.Precision
|
||||
};
|
||||
return entity.ToT(_mapper, create, null,false);
|
||||
}
|
||||
|
||||
public static DataSourceEntity ToEntity(this DataSourceTinyDto dto)
|
||||
{
|
||||
Func<DataSourceTinyDto, DataSourceEntity> create = dataSource => new DataSourceEntity
|
||||
{
|
||||
IdSource = dataSource.Id,
|
||||
Type = dataSource.Type,
|
||||
Model = dataSource.Model,
|
||||
Precision = dataSource.Precision
|
||||
};
|
||||
return dto.ToU(_mapper, create);
|
||||
}
|
||||
|
||||
public static ResponseDataSourceDto ToResponseDto(this DataSourceEntity entity)
|
||||
{
|
||||
Func<DataSourceEntity, ResponseDataSourceDto> create = dataSourceEntity => new ResponseDataSourceDto
|
||||
{
|
||||
Id = dataSourceEntity.IdSource,
|
||||
Type = dataSourceEntity.Type,
|
||||
Model = dataSourceEntity.Model,
|
||||
Precision = dataSourceEntity.Precision,
|
||||
};
|
||||
|
||||
Action<DataSourceEntity, ResponseDataSourceDto> linker = (dataSourceEntity, dto) =>
|
||||
{
|
||||
dto.Activities = dataSourceEntity.Activities.ToTinyDtos().ToArray();
|
||||
dto.Users = dataSourceEntity.Athletes.ToTinyDtos().ToArray();
|
||||
};
|
||||
|
||||
return entity.ToT(_mapperFull, create, linker, false);
|
||||
}
|
||||
|
||||
public static IEnumerable<DataSourceTinyDto> ToTinyDtos(this IEnumerable<DataSourceEntity> entities)
|
||||
=> entities.Select(e => e.ToTinyDto());
|
||||
|
||||
public static IEnumerable<DataSourceEntity> ToEntities(this IEnumerable<DataSourceTinyDto> dtos)
|
||||
=> dtos.Select(d => d.ToEntity());
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using Dto.Tiny;
|
||||
using Entities;
|
||||
using Shared;
|
||||
|
||||
namespace Entities2Dto;
|
||||
|
||||
public static class FriendshipMapper
|
||||
{
|
||||
private static GenericMapper<FriendshipDto, FriendshipEntity> _mapper = new();
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_mapper.Reset();
|
||||
}
|
||||
|
||||
public static FriendshipDto ToTinyDto(this FriendshipEntity entity)
|
||||
{
|
||||
Func<FriendshipEntity, FriendshipDto> create = friendshipEntity => new FriendshipDto
|
||||
{
|
||||
FollowedId = friendshipEntity.FollowingId,
|
||||
FollowerId = friendshipEntity.FollowerId,
|
||||
};
|
||||
|
||||
return entity.ToT(_mapper, create, null, false);
|
||||
}
|
||||
|
||||
public static IEnumerable<FriendshipDto> ToTinyDtos(this IEnumerable<FriendshipEntity> entities)
|
||||
=> entities.Select(e => e.ToTinyDto());
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
using Dto.Tiny;
|
||||
using Entities;
|
||||
using Shared;
|
||||
|
||||
namespace Entities2Dto;
|
||||
|
||||
public static class HeartRateMapper
|
||||
{
|
||||
|
||||
private static GenericMapper<HeartRateTinyDto, HeartRateEntity> _mapper = new();
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_mapper.Reset();
|
||||
}
|
||||
|
||||
public static HeartRateTinyDto ToTinyDto(this HeartRateEntity entity)
|
||||
{
|
||||
var activityTmp = new DateTime();
|
||||
Func<HeartRateEntity, HeartRateTinyDto> create = heartRateEntity => new HeartRateTinyDto
|
||||
{
|
||||
Id = heartRateEntity.IdHeartRate,
|
||||
HeartRate = heartRateEntity.Bpm,
|
||||
Timestamp = activityTmp,
|
||||
Latitude = heartRateEntity.Latitude,
|
||||
Longitude = heartRateEntity.Longitude,
|
||||
Altitude = heartRateEntity.Altitude,
|
||||
Cadence = heartRateEntity.Cadence,
|
||||
Distance = heartRateEntity.Distance,
|
||||
Speed = heartRateEntity.Speed,
|
||||
Power = heartRateEntity.Power,
|
||||
Temperature = heartRateEntity.Temperature
|
||||
};
|
||||
|
||||
return entity.ToT(_mapper, create, null, false);
|
||||
}
|
||||
|
||||
public static HeartRateEntity ToEntity(this HeartRateTinyDto dto)
|
||||
{
|
||||
Func<HeartRateTinyDto, HeartRateEntity> create = heartRate => new HeartRateEntity
|
||||
{
|
||||
IdHeartRate = heartRate.Id,
|
||||
Bpm = heartRate.HeartRate,
|
||||
Time = TimeOnly.FromDateTime(heartRate.Timestamp),
|
||||
Latitude = heartRate.Latitude,
|
||||
Longitude = heartRate.Longitude,
|
||||
Altitude = heartRate.Altitude,
|
||||
Cadence = heartRate.Cadence,
|
||||
Distance = heartRate.Distance,
|
||||
Speed = heartRate.Speed,
|
||||
Power = heartRate.Power,
|
||||
Temperature = heartRate.Temperature
|
||||
};
|
||||
return dto.ToU(_mapper, create);
|
||||
}
|
||||
|
||||
public static IEnumerable<HeartRateTinyDto> ToTinyDtos(this IEnumerable<HeartRateEntity> entities)
|
||||
=> entities.Select(e => e.ToTinyDto());
|
||||
|
||||
public static IEnumerable<HeartRateEntity> ToEntities(this IEnumerable<HeartRateTinyDto> dtos)
|
||||
=> dtos.Select(d => d.ToEntity());
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using Dto;
|
||||
using Entities;
|
||||
using Shared;
|
||||
|
||||
namespace Entities2Dto;
|
||||
|
||||
public static class LargeImageMapper
|
||||
{
|
||||
private static GenericMapper<LargeImageDto, LargeImageEntity> _mapper = new();
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_mapper.Reset();
|
||||
}
|
||||
|
||||
public static LargeImageDto ToDto(this LargeImageEntity entity)
|
||||
{
|
||||
Func<LargeImageEntity, LargeImageDto> create = largeImageEntity => new() { Base64 = largeImageEntity.Base64 };
|
||||
|
||||
return entity.ToT(_mapper, create, null, false);
|
||||
}
|
||||
|
||||
public static LargeImageEntity ToEntity(this LargeImageDto dto)
|
||||
{
|
||||
Func<LargeImageDto, LargeImageEntity> create = largeImage => new LargeImageEntity
|
||||
{
|
||||
Base64 = largeImage.Base64
|
||||
};
|
||||
|
||||
return dto.ToU(_mapper, create);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
using Dto;
|
||||
using Dto.Tiny;
|
||||
using Entities;
|
||||
using Shared;
|
||||
|
||||
namespace Entities2Dto;
|
||||
|
||||
public static class UserMappeur
|
||||
{
|
||||
private static GenericMapper<UserTinyDto, AthleteEntity> _mapper = new();
|
||||
private static GenericMapper<ResponseUserDto, AthleteEntity> _mapperFull = new();
|
||||
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_mapper.Reset();
|
||||
_mapperFull.Reset();
|
||||
}
|
||||
|
||||
public static UserTinyDto ToTinyDto(this AthleteEntity entity)
|
||||
{
|
||||
Func<AthleteEntity, UserTinyDto> create = athleteEntity => new UserTinyDto
|
||||
{
|
||||
Id = athleteEntity.Id,
|
||||
FirstName = athleteEntity.FirstName,
|
||||
LastName = athleteEntity.LastName,
|
||||
Email = athleteEntity.Email,
|
||||
Password = athleteEntity.PasswordHash,
|
||||
DateOfBirth = athleteEntity.DateOfBirth.ToDateTime(TimeOnly.MinValue),
|
||||
Sexe = athleteEntity.Sexe,
|
||||
Username = athleteEntity.UserName,
|
||||
Weight = athleteEntity.Weight,
|
||||
Length = (float)athleteEntity.Length,
|
||||
ProfilePicture = athleteEntity.ProfilPicture ?? "",
|
||||
IsCoach = athleteEntity.IsCoach
|
||||
};
|
||||
|
||||
return entity.ToT(_mapper, create, null, false);
|
||||
}
|
||||
|
||||
public static AthleteEntity ToEntity(this UserTinyDto model)
|
||||
{
|
||||
Func<UserTinyDto, AthleteEntity> create = user => new AthleteEntity
|
||||
{
|
||||
Id = user.Id,
|
||||
UserName = user.Username,
|
||||
Sexe = user.Sexe,
|
||||
FirstName = user.FirstName,
|
||||
LastName = user.LastName,
|
||||
Email = user.Email,
|
||||
PasswordHash = user.Password ?? "",
|
||||
DateOfBirth = DateOnly.FromDateTime(user.DateOfBirth),
|
||||
IsCoach = user.IsCoach,
|
||||
Weight = user.Weight,
|
||||
Length = user.Length,
|
||||
ProfilPicture = user.ProfilePicture
|
||||
};
|
||||
|
||||
return model.ToU(_mapper, create);
|
||||
}
|
||||
|
||||
public static ResponseUserDto ToResponseDto(this AthleteEntity entity)
|
||||
{
|
||||
Func<AthleteEntity, ResponseUserDto> creator = athleteEntity => new ResponseUserDto
|
||||
{
|
||||
Id = athleteEntity.Id,
|
||||
FirstName = athleteEntity.FirstName,
|
||||
LastName = athleteEntity.LastName,
|
||||
Email = athleteEntity.Email,
|
||||
Password = athleteEntity.PasswordHash,
|
||||
DateOfBirth = athleteEntity.DateOfBirth.ToDateTime(TimeOnly.MinValue),
|
||||
Sexe = athleteEntity.Sexe,
|
||||
Username = athleteEntity.UserName,
|
||||
Weight = athleteEntity.Weight,
|
||||
Lenght = (float)athleteEntity.Length,
|
||||
ProfilePicture = athleteEntity.ProfilPicture ?? "",
|
||||
IsCoach = athleteEntity.IsCoach,
|
||||
};
|
||||
|
||||
Action<AthleteEntity, ResponseUserDto> linker = (athleteEntity, userDto) =>
|
||||
{
|
||||
userDto.Activities = athleteEntity.Activities.ToTinyDtos().ToArray();
|
||||
userDto.Image = athleteEntity.Image?.ToDto();
|
||||
userDto.DataSource = athleteEntity.DataSource?.ToTinyDto();
|
||||
userDto.Followers = athleteEntity.Followers.ToTinyDtos().ToArray();
|
||||
userDto.Followings = athleteEntity.Followings.ToTinyDtos().ToArray();
|
||||
};
|
||||
|
||||
return entity.ToT(_mapperFull, creator, linker, false);
|
||||
}
|
||||
|
||||
public static IEnumerable<UserTinyDto> ToTinyDtos(this IEnumerable<AthleteEntity> entities)
|
||||
=> entities.Select(e => e.ToTinyDto());
|
||||
|
||||
public static IEnumerable<AthleteEntity> ToEntities(this IEnumerable<UserTinyDto> models)
|
||||
=> models.Select(m => m.ToEntity());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
using Dto;
|
||||
using Dto.Tiny;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Model.Manager;
|
||||
using Model.Repository;
|
||||
using Model.utils;
|
||||
|
||||
namespace HeartTrackAPI.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/[controller]")]
|
||||
[Authorize]
|
||||
public class AnalysisController : Controller
|
||||
{
|
||||
|
||||
private readonly IActivityRepository _activityService;
|
||||
private readonly ILogger<AnalysisController> _logger;
|
||||
|
||||
|
||||
public AnalysisController(IDataManager dataManager, ILogger<AnalysisController> logger)
|
||||
{
|
||||
_activityService = dataManager.ActivityRepo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet("activity/{activityId}")]
|
||||
public async Task<IActionResult> AnalyseByActivityId(int activityId)
|
||||
{
|
||||
var activity = await _activityService.GetActivityById(activityId);
|
||||
if (activity == null)
|
||||
{
|
||||
_logger.LogInformation($"Activity with ID {activityId} not found.");
|
||||
return NotFound($"Activity with ID {activityId} not found.");
|
||||
}
|
||||
// for the moment no need to get the user Entity [Dave]
|
||||
var user = activity.Athlete;
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogInformation($"User not found for activity ID {activityId}.");
|
||||
return NotFound($"User not found for activity ID {activityId}.");
|
||||
}
|
||||
|
||||
var analysis = ActivityAnalysis.FromActivityData(activity);
|
||||
return Ok(analysis);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
public class HeartRateZoneResult
|
||||
{
|
||||
public string Zone { get; set; }
|
||||
public TimeSpan TimeSpent { get; set; }
|
||||
}
|
||||
|
||||
private readonly List<HeartRateZone> _heartRateZones = new()
|
||||
{
|
||||
new() { Name = "Repos", MinHeartRate = 0, MaxHeartRate = 60 },
|
||||
new() { Name = "Aérobie légère", MinHeartRate = 61, MaxHeartRate = 90 },
|
||||
new() { Name = "Aérobie", MinHeartRate = 91, MaxHeartRate = 140 },
|
||||
new() { Name = "Anaérobie", MinHeartRate = 141, MaxHeartRate = 180 },
|
||||
new() { Name = "VO2 Max", MinHeartRate = 181, MaxHeartRate = 220 }
|
||||
};
|
||||
[HttpGet("heart-rate/zones/{activityId}")]
|
||||
public IActionResult GetActivityHeartRateZones(int activityId)
|
||||
{
|
||||
var heartRateTinyDtos = _activityService.GetActivityById(activityId).Result?.HeartRates;
|
||||
if (heartRateTinyDtos != null)
|
||||
{
|
||||
var heartRates = heartRateTinyDtos.ToList();
|
||||
var results = _heartRateZones.Select(zone => new HeartRateZoneResult
|
||||
{
|
||||
Zone = zone.Name,
|
||||
TimeSpent = CalculateTimeInZone(zone, heartRates)
|
||||
}).ToList();
|
||||
|
||||
return Ok(results);
|
||||
}
|
||||
|
||||
return NotFound("Not heart rates");
|
||||
|
||||
}
|
||||
private TimeSpan CalculateTimeInZone(HeartRateZone zone, List<HeartRateTinyDto> heartRates)
|
||||
{
|
||||
var secondsInZone =
|
||||
heartRates.Count(hr => hr.HeartRate >= zone.MinHeartRate && hr.HeartRate <= zone.MaxHeartRate);
|
||||
return TimeSpan.FromSeconds(secondsInZone);
|
||||
}* /
|
||||
|
||||
[HttpGet("getOptimizedPath")]
|
||||
public IActionResult GetOptimizedPath(int activityId)
|
||||
{
|
||||
var heartRateData = GetMockHeartRateData();
|
||||
|
||||
var sortedData = heartRateData.OrderBy(x => x.Timestamp).ToList();
|
||||
|
||||
var path = new GeoJsonPath();
|
||||
foreach (var item in sortedData)
|
||||
{
|
||||
if (item.Latitude.HasValue && item.Longitude.HasValue)
|
||||
{
|
||||
path.Coordinates.Add(new GeoJsonCoordinate(item.Longitude.Value, item.Latitude.Value));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(path.ToGeoJson());
|
||||
}
|
||||
*/
|
@ -0,0 +1,154 @@
|
||||
using System.Globalization;
|
||||
using Dto.Auth;
|
||||
using Dto.Tiny;
|
||||
using Entities;
|
||||
using HeartTrackAPI.Request;
|
||||
using HeartTrackAPI.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace HeartTrackAPI.Controllers;
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
public class AuthController : Controller
|
||||
{
|
||||
|
||||
private readonly UserManager<AthleteEntity> _userManager;
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly SignInManager<AthleteEntity> _signinManager;
|
||||
|
||||
public AuthController(UserManager<AthleteEntity> userManager,ITokenService tokenService, SignInManager<AthleteEntity> signinManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_tokenService = tokenService;
|
||||
_signinManager = signinManager;
|
||||
}
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login(LoginRequestDto loginDto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(ModelState);
|
||||
|
||||
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == loginDto.Username.ToLower());
|
||||
|
||||
if (user == null) return Unauthorized("Invalid username!");
|
||||
|
||||
var result = await _signinManager.CheckPasswordSignInAsync(user, loginDto.Password, false);
|
||||
|
||||
if (!result.Succeeded) return Unauthorized("Username not found and/or password incorrect");
|
||||
|
||||
return Ok(new AuthResponseDto
|
||||
{
|
||||
AccessToken = _tokenService.CreateToken(user),
|
||||
ExpiresIn = DateTime.Now.AddDays(7).ToString(CultureInfo.InvariantCulture),
|
||||
TokenType = "Bearer"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequestDto request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(ModelState);
|
||||
// just for testing
|
||||
// the good way is to use the repository and give him the userManager
|
||||
var user = new AthleteEntity
|
||||
{
|
||||
Email = request.Email,
|
||||
UserName = request.Username,
|
||||
LastName = request.LastName,
|
||||
FirstName = request.FirstName,
|
||||
Sexe = request.Sexe,
|
||||
Length = request.Size,
|
||||
Weight = request.Weight,
|
||||
DateOfBirth = DateOnly.FromDateTime(request.DateOfBirth),
|
||||
IsCoach = request.IsCoach
|
||||
};
|
||||
var createdUser = _userManager.CreateAsync(user, request.Password).Result;
|
||||
if (createdUser.Succeeded)
|
||||
{
|
||||
var roleResult = await _userManager.AddToRoleAsync(user, request.IsCoach ? "Coach" : "Athlete");
|
||||
if (roleResult.Succeeded)
|
||||
{
|
||||
return Ok(
|
||||
new AuthResponseDto
|
||||
{
|
||||
AccessToken = _tokenService.CreateToken(user),
|
||||
ExpiresIn = DateTime.Now.AddDays(7).ToString(),
|
||||
TokenType = "Bearer"
|
||||
}
|
||||
);
|
||||
}
|
||||
{
|
||||
return StatusCode(500, roleResult.Errors);
|
||||
}
|
||||
}
|
||||
{
|
||||
return StatusCode(500, createdUser.Errors);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
|
||||
|
||||
/* var user = _userRepository.GetByEmail(request.Email);
|
||||
if (user != null)
|
||||
{
|
||||
return BadRequest("User already exists");
|
||||
}
|
||||
var newUser = new User
|
||||
{
|
||||
Email = request.Email,
|
||||
PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.PasswordHash),
|
||||
FirstName = request.FirstName,
|
||||
LastName = request.LastName
|
||||
};
|
||||
_userRepository.Add(newUser);
|
||||
return Ok();*/
|
||||
}
|
||||
/*
|
||||
[HttpPost("refresh")]
|
||||
public IActionResult Refresh([FromBody] RefreshRequest request)
|
||||
{
|
||||
var user = _userRepository.GetByEmail(request.Email);
|
||||
if (user == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
if (!BCrypt.Net.BCrypt.Verify(request.PasswordHash, user.PasswordHash))
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
var token = _jwtService.GenerateToken(user);
|
||||
return Ok(new { token });
|
||||
}
|
||||
*/
|
||||
[HttpPost("logout")]
|
||||
public IActionResult Logout()
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
/*
|
||||
|
||||
[HttpPost("forgot-password")]
|
||||
public IActionResult ForgotPassword([FromBody] ForgotPasswordRequest request)
|
||||
{
|
||||
var user = _userRepository.GetByEmail(request.Email);
|
||||
if (user == null)
|
||||
{
|
||||
return BadRequest("User not found");
|
||||
}
|
||||
var token = _jwtService.GenerateToken(user);
|
||||
// send email with token
|
||||
return Ok();
|
||||
}*/
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Entities;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace HeartTrackAPI.Services;
|
||||
public interface ITokenService
|
||||
{
|
||||
string CreateToken(AthleteEntity user);
|
||||
}
|
||||
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
private readonly SymmetricSecurityKey _key;
|
||||
|
||||
public TokenService(IConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:SigningKey"]));
|
||||
}
|
||||
public string CreateToken(AthleteEntity user)
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new (JwtRegisteredClaimNames.Email, user.Email),
|
||||
new (JwtRegisteredClaimNames.NameId, user.Id.ToString()),
|
||||
new (JwtRegisteredClaimNames.GivenName, user.UserName)
|
||||
};
|
||||
|
||||
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
|
||||
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(claims),
|
||||
Expires = DateTime.Now.AddDays(7),
|
||||
SigningCredentials = creds,
|
||||
Issuer = _config["JWT:Issuer"],
|
||||
Audience = _config["JWT:Audience"]
|
||||
};
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace Model.Repository;
|
||||
|
||||
public interface IDataSourceRepository<T>
|
||||
{
|
||||
Task<T?> GetItemById(int id);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
using Model.utils;
|
||||
|
||||
namespace Dto.Tiny;
|
||||
|
||||
public class ActivityAnalysis
|
||||
{
|
||||
public double AverageHeartRate { get; private set; }
|
||||
public string AverageHeartRateAdvice { get; private set; }
|
||||
public double Vo2Max { get; private set; }
|
||||
public string Vo2MaxAdvice { get; private set; }
|
||||
public double NormalBpm { get; private set; }
|
||||
public string NormalBpmAdvice { get; private set; }
|
||||
public double HeartRateVariability { get; private set; }
|
||||
public string HeartRateVariabilityAdvice { get; private set; }
|
||||
public string HeartRateZone { get; private set; }
|
||||
public string HeartRateZoneAdvice { get; private set; }
|
||||
public double Duration { get; private set; }
|
||||
public string DurationAdvice { get; private set; }
|
||||
public double Effort { get; private set; }
|
||||
public string EffortAdvice { get; private set; }
|
||||
public static ActivityAnalysis FromActivityData(ResponseActivityDto activity)
|
||||
{
|
||||
double dureeActivity = (activity.EndTime - activity.StartTime).TotalMinutes;
|
||||
var age = DateTime.Today.Year - activity.Athlete.DateOfBirth.Year;
|
||||
var gender = activity.Athlete.Sexe;
|
||||
var poids = activity.Athlete.Weight;
|
||||
var effortFelt = activity.EffortFelt;
|
||||
var averageHeartRate = activity.Average;
|
||||
var heartRateVariability = activity.Variability;
|
||||
|
||||
var heartRateZones = HeartRateAdvise.CalculateHeartRateZones(age);
|
||||
|
||||
var effortScore = HeartRateAdvise.EvaluateEffort(activity);
|
||||
|
||||
var (seuilBPM, vo2Max) = HeartRateAdvise.SeuilBPMavance(gender, age, poids, dureeActivity);
|
||||
string averageHeartRateAdvice = HeartRateAdvise.GenerateAverageHeartRateAdvice(averageHeartRate, seuilBPM);
|
||||
string vo2MaxAdvice = HeartRateAdvise.GenerateVo2MaxAdvice(vo2Max);
|
||||
string normalBpmAdvice = HeartRateAdvise.GenerateNormalBpmAdvice(seuilBPM, averageHeartRate);
|
||||
string hrvAdvice = HeartRateAdvise.GenerateHrvAdvice(heartRateVariability);
|
||||
HeartRateAdvise.HeartRateZone currentZone = heartRateZones.Find(zone => averageHeartRate >= zone.MinHeartRate && averageHeartRate <= zone.MaxHeartRate);
|
||||
string heartRateZoneAdvice = HeartRateAdvise.GenerateHeartRateZoneAdvice(currentZone?.Name);
|
||||
var effortAccuracy = HeartRateAdvise.CompareEffort(effortFelt, (int)effortScore);
|
||||
var analysis = new ActivityAnalysis
|
||||
{
|
||||
AverageHeartRate = averageHeartRate,
|
||||
AverageHeartRateAdvice = averageHeartRateAdvice,
|
||||
Vo2Max = vo2Max,
|
||||
Vo2MaxAdvice = vo2MaxAdvice,
|
||||
NormalBpm = seuilBPM,
|
||||
NormalBpmAdvice = normalBpmAdvice,
|
||||
HeartRateVariability = heartRateVariability,
|
||||
HeartRateVariabilityAdvice = hrvAdvice,
|
||||
HeartRateZone = currentZone != null ? currentZone.Name : "N/A",
|
||||
HeartRateZoneAdvice = heartRateZoneAdvice,
|
||||
Duration = dureeActivity,
|
||||
DurationAdvice =HeartRateAdvise.GenerateDurationAdvice(dureeActivity),
|
||||
Effort = effortScore,
|
||||
EffortAdvice = HeartRateAdvise.GenerateEffortAdvice(effortAccuracy)
|
||||
};
|
||||
|
||||
return analysis;
|
||||
}
|
||||
}
|
@ -0,0 +1,278 @@
|
||||
using Dto;
|
||||
|
||||
namespace Model.utils;
|
||||
|
||||
public class HeartRateAdvise
|
||||
{
|
||||
public class HeartRateZone
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int MinHeartRate { get; set; }
|
||||
public int MaxHeartRate { get; set; }
|
||||
}
|
||||
|
||||
public string getAdvise(Activity activity)
|
||||
{
|
||||
return "You should take a break";
|
||||
}
|
||||
public static List<HeartRateZone> CalculateHeartRateZones(int age)
|
||||
{
|
||||
int fcm = 220 - age; // Estimation de la FCM
|
||||
List<HeartRateZone> zones = new List<HeartRateZone>
|
||||
{
|
||||
new HeartRateZone { Name = "Très Légère", MinHeartRate = (int)(fcm * 0.50), MaxHeartRate = (int)(fcm * 0.60) },
|
||||
new HeartRateZone { Name = "Aérobie légère", MinHeartRate = (int)(fcm * 0.60), MaxHeartRate = (int)(fcm * 0.70) },
|
||||
new HeartRateZone { Name = "Aérobie", MinHeartRate = (int)(fcm * 0.70), MaxHeartRate = (int)(fcm * 0.80) },
|
||||
new HeartRateZone { Name = "Anaérobie", MinHeartRate = (int)(fcm * 0.80), MaxHeartRate = (int)(fcm * 0.90) },
|
||||
new HeartRateZone { Name = "Maximum", MinHeartRate = (int)(fcm * 0.90), MaxHeartRate = (int)(fcm * 1.00) }
|
||||
};
|
||||
|
||||
return zones;
|
||||
}
|
||||
public static double EvaluateEffort(ResponseActivityDto stats)
|
||||
{
|
||||
double score = 0;
|
||||
|
||||
score += stats.Average * 0.3; // Exemple de poids
|
||||
score += stats.HeartRates.Average(speed => speed.Speed ?? 0) * 0.25;
|
||||
score += stats.HeartRates.Sum(dist => dist.Distance ?? 0) * 0.15;
|
||||
score += stats.HeartRates.Average(hr => hr.Cadence ?? 0) * 0.1;
|
||||
score += stats.HeartRates.Average(hr => hr.Power ?? 0) * 0.15;
|
||||
score += stats.StandardDeviation * 0.05;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public static string CompareEffort(int perceivedEffort, int objectiveEffort)
|
||||
{
|
||||
if (perceivedEffort == objectiveEffort) return "Accurate";
|
||||
if (perceivedEffort > objectiveEffort) return "Overestimated";
|
||||
if (perceivedEffort < objectiveEffort) return "Underestimated";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
|
||||
// Faible intensité : 50-60% de la FCM
|
||||
// Intensité modérée : 60-70% de la FCM
|
||||
// Haute intensité : 70-85% de la FCM
|
||||
public static double CalculerVo2Max(char sexe, int age, double poids)
|
||||
{
|
||||
if (sexe == 'M')
|
||||
{
|
||||
return (poids * 0.198) + (age * -0.193) + 24.489;
|
||||
}
|
||||
|
||||
// sexe == "F"
|
||||
{
|
||||
return (poids * 0.201) + (age * -0.217) + 20.453;
|
||||
}
|
||||
}
|
||||
|
||||
public static (double SeuilBPM, double Vo2Max) SeuilBPMavance(char sexe, int age, double poids, double dureeActivite)
|
||||
{
|
||||
double fcm = sexe == 'M' ? 207 - (0.7 * age) : 206 - (0.88 * age);
|
||||
double vo2Max = CalculerVo2Max(sexe, age, poids);
|
||||
|
||||
double zone = dureeActivite <= 30 ? 0.8 : dureeActivite <= 60 ? 0.7 : 0.6;
|
||||
double seuilBpm = fcm * zone;
|
||||
|
||||
return (seuilBpm, vo2Max);
|
||||
}
|
||||
|
||||
public static Dictionary<string, (double Value, string Advice)> GenerateAdvice(ResponseActivityDto activity)
|
||||
{
|
||||
int dureeActivity = (activity.EndTime - activity.StartTime).Minutes;
|
||||
var age = DateTime.Today.Year - activity.Athlete.DateOfBirth.Year;
|
||||
var gender = activity.Athlete.Sexe;
|
||||
var poids = activity.Athlete.Weight;
|
||||
var effortFelt = activity.EffortFelt;
|
||||
var averageHeartRate = activity.Average;
|
||||
var heartRateVariability = activity.Variability;
|
||||
var heartRateZones = CalculateHeartRateZones(age);
|
||||
var effortScore = EvaluateEffort(activity);
|
||||
|
||||
|
||||
var (seuilBPM, vo2Max) = SeuilBPMavance(gender, age, poids, dureeActivity);
|
||||
Dictionary<string, (double Value, string Advice)> healthMetrics = new Dictionary<string, (double Value, string Advice)>();
|
||||
|
||||
// Conseil pour AverageHeartRate
|
||||
string averageHeartRateAdvice = averageHeartRate > seuilBPM
|
||||
? "Votre rythme cardiaque moyen est supérieur au seuil recommandé. Envisagez de réduire l'intensité."
|
||||
: "Votre rythme cardiaque moyen est dans une bonne plage. Continuez à maintenir votre intensité actuelle.";
|
||||
|
||||
// Conseil pour Vo2Max
|
||||
string vo2MaxAdvice;
|
||||
if (vo2Max < 30)
|
||||
vo2MaxAdvice =
|
||||
"Votre Vo2 max est faible, envisagez d'augmenter progressivement l'intensité de vos entraînements.";
|
||||
else if (vo2Max < 40)
|
||||
vo2MaxAdvice = "Votre Vo2 max est dans la moyenne. Continuez de travailler sur votre endurance.";
|
||||
else vo2MaxAdvice = "Votre Vo2 max est excellente. Vous pourriez bénéficier d'entraînements à haute intensité.";
|
||||
|
||||
// Conseil basé sur la comparaison avec NormalBpm
|
||||
string normalBpmAdvice = seuilBPM > averageHeartRate
|
||||
? "Votre BPM normal est plus élevé que la moyenne, assurez-vous de surveiller votre intensité d'entraînement."
|
||||
: "Votre BPM normal est inférieur à la moyenne, ce qui peut indiquer un bon niveau de forme physique.";
|
||||
// Conseil pour HeartRateVariability
|
||||
string hrvAdvice = heartRateVariability < 40
|
||||
? "Votre HRV est basse, ce qui peut indiquer un stress élevé ou une récupération insuffisante. Envisagez d'améliorer votre récupération."
|
||||
: heartRateVariability < 60
|
||||
? "Votre HRV est dans une plage moyenne. Continuez de surveiller votre récupération et votre stress."
|
||||
: "Votre HRV est élevée, indiquant une bonne récupération et une gestion du stress. Continuez vos bonnes pratiques de gestion de la santé.";
|
||||
|
||||
HeartRateZone currentZone = heartRateZones.Find(zone =>
|
||||
averageHeartRate >= zone.MinHeartRate && averageHeartRate <= zone.MaxHeartRate);
|
||||
string heartRateZoneAdvice;
|
||||
if (currentZone != null)
|
||||
{
|
||||
heartRateZoneAdvice = $"Votre BPM moyen est dans la zone '{currentZone.Name}', qui est idéale pour ";
|
||||
switch (currentZone.Name)
|
||||
{
|
||||
case "Repos":
|
||||
heartRateZoneAdvice += "favoriser la récupération.";
|
||||
break;
|
||||
case "Endurance":
|
||||
heartRateZoneAdvice += "améliorer l'endurance cardiovasculaire et brûler des graisses.";
|
||||
break;
|
||||
case "Aérobie":
|
||||
heartRateZoneAdvice += "améliorer votre capacité aérobie.";
|
||||
break;
|
||||
case "Anaérobie":
|
||||
heartRateZoneAdvice += "améliorer la performance à haute intensité.";
|
||||
break;
|
||||
case "VO2 Max":
|
||||
heartRateZoneAdvice += "maximiser la performance et la capacité aérobie.";
|
||||
break;
|
||||
default:
|
||||
heartRateZoneAdvice =
|
||||
"Cette zone est spécifique et peut avoir différents objectifs en fonction de votre plan d'entraînement.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
heartRateZoneAdvice =
|
||||
"Vous n'êtes dans aucune zone spécifique. Assurez-vous que votre BPM moyen est correctement mesuré.";
|
||||
}
|
||||
|
||||
// Comparaison de l'effort objectif avec l'effort perçu
|
||||
var effortAccuracy = CompareEffort(effortFelt, (int)effortScore);
|
||||
|
||||
// Remplissage du dictionnaire
|
||||
healthMetrics.Add("AverageHeartRate", (averageHeartRate, averageHeartRateAdvice));
|
||||
healthMetrics.Add("Vo2Max", (vo2Max, vo2MaxAdvice));
|
||||
healthMetrics.Add("NormalBpm", (seuilBPM, normalBpmAdvice));
|
||||
healthMetrics.Add("HeartRateVariability", (heartRateVariability, hrvAdvice));
|
||||
healthMetrics.Add("HeartRateZone", (averageHeartRate, heartRateZoneAdvice));
|
||||
healthMetrics.Add("Duration",
|
||||
(dureeActivity,
|
||||
dureeActivity < 75
|
||||
? "Vous pourriez augmenter la durée de vos activités pour atteindre les recommandations."
|
||||
: "Votre durée d'activité est conforme aux recommandations."));
|
||||
healthMetrics.Add("Effort", (effortScore, GenerateEffortAdvice(effortAccuracy)));
|
||||
return healthMetrics;
|
||||
}
|
||||
public static string GenerateAverageHeartRateAdvice(float averageHeartRate, double seuilBPM)
|
||||
{
|
||||
return averageHeartRate > seuilBPM
|
||||
? "Votre rythme cardiaque moyen est supérieur au seuil recommandé. Envisagez de réduire l'intensité."
|
||||
: "Votre rythme cardiaque moyen est dans une bonne plage. Continuez à maintenir votre intensité actuelle.";
|
||||
}
|
||||
public static string GenerateVo2MaxAdvice(double vo2Max)
|
||||
{
|
||||
if (vo2Max < 30)
|
||||
return "Votre Vo2 max est faible, envisagez d'augmenter progressivement l'intensité de vos entraînements.";
|
||||
if (vo2Max < 40)
|
||||
return "Votre Vo2 max est dans la moyenne. Continuez de travailler sur votre endurance.";
|
||||
return "Votre Vo2 max est excellente. Vous pourriez bénéficier d'entraînements à haute intensité.";
|
||||
}
|
||||
public static string GenerateNormalBpmAdvice(double normalBpm,double averageHeartRate)
|
||||
{
|
||||
return normalBpm > averageHeartRate
|
||||
? "Votre BPM normal est plus élevé que la moyenne, ce qui peut indiquer un bon niveau de forme physique."
|
||||
: "Votre BPM normal est inférieur à la moyenne, assurez-vous de surveiller votre intensité d'entraînement.";
|
||||
}
|
||||
|
||||
public static string GenerateHrvAdvice(double heartRateVariability)
|
||||
{
|
||||
return heartRateVariability < 40
|
||||
? "Votre HRV est basse, ce qui peut indiquer un stress élevé ou une récupération insuffisante. Envisagez d'améliorer votre récupération."
|
||||
: heartRateVariability < 60
|
||||
? "Votre HRV est dans une plage moyenne. Continuez de surveiller votre récupération et votre stress."
|
||||
: "Votre HRV est élevée, indiquant une bonne récupération et une gestion du stress. Continuez vos bonnes pratiques de gestion de la santé.";
|
||||
}
|
||||
public static string GenerateHeartRateZoneAdvice(string? heartRateZone)
|
||||
{
|
||||
return heartRateZone switch
|
||||
{
|
||||
"Repos" => "Favoriser la récupération.",
|
||||
"Endurance" => "Améliorer l'endurance cardiovasculaire et brûler des graisses.",
|
||||
"Aérobie" => "Améliorer votre capacité aérobie.",
|
||||
"Anaérobie" => "Améliorer la performance à haute intensité.",
|
||||
"VO2 Max" => "Maximiser la performance et la capacité aérobie.",
|
||||
_ => "Cette zone est spécifique et peut avoir différents objectifs en fonction de votre plan d'entraînement."
|
||||
};
|
||||
}
|
||||
public static string GenerateDurationAdvice(double dureeActivity)
|
||||
{
|
||||
return dureeActivity < 75
|
||||
? "Vous pourriez augmenter la durée de vos activités pour atteindre les recommandations."
|
||||
: "Votre durée d'activité est conforme aux recommandations.";
|
||||
}
|
||||
|
||||
public static string GenerateEffortAdvice(string effortAccuracy)
|
||||
{
|
||||
return effortAccuracy switch
|
||||
{
|
||||
"Accurate" => "Votre perception de l'effort est précise. Continuez ainsi!",
|
||||
"Overestimated" => "Vous surestimez votre effort. Essayez de pousser un peu plus lors de vos prochaines activités.",
|
||||
"Underestimated" => "Vous sous-estimez votre effort. Vous pourriez être plus proche de vos limites que vous ne le pensez.",
|
||||
_ => "Continuez à surveiller et ajuster votre effort pour de meilleurs résultats."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public class EffortAnalysisResult
|
||||
{
|
||||
public string EffortAccuracy { get; set; }
|
||||
public string Advice { get; set; }
|
||||
}
|
||||
|
||||
public EffortAnalysisResult AnalyzeActivityEffort(ResponseActivityDto activity)
|
||||
{
|
||||
|
||||
|
||||
var effortScore = EvaluateEffort(activity);
|
||||
|
||||
// Comparaison de l'effort objectif avec l'effort perçu
|
||||
var effortAccuracy = CompareEffort(activity.EffortFelt, (int)effortScore);
|
||||
var result = new EffortAnalysisResult
|
||||
{
|
||||
EffortAccuracy = effortAccuracy,
|
||||
Advice = GenerateEffortAdvice(effortAccuracy)
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
public List<HeartRateTinyDto> GetMockHeartRateData()
|
||||
{
|
||||
var random = new Random();
|
||||
return Enumerable.Range(1, 3600)
|
||||
.Select(_ => new HeartRateTinyDto
|
||||
{
|
||||
HeartRate = random.Next(60, 220),
|
||||
Timestamp = new DateTime(2021, 1, 1).AddSeconds(random.Next(3600)),
|
||||
Latitude = random.NextDouble() * 180 - 90,
|
||||
Longitude = random.NextDouble() * 360 - 180,
|
||||
Altitude = random.NextDouble() * 1000,
|
||||
Cadence = random.Next(60, 120),
|
||||
Distance = random.NextDouble() * 100,
|
||||
Speed = random.NextDouble() * 30,
|
||||
Power = random.Next(0, 500),
|
||||
Temperature = random.NextDouble() * 30
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
*/
|
||||
|
@ -0,0 +1,34 @@
|
||||
using Dto.Tiny;
|
||||
using Entities2Dto;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Model.Repository;
|
||||
|
||||
namespace Model2Entities;
|
||||
|
||||
public partial class DbDataManager
|
||||
{
|
||||
public class DataSourceRepository : IDataSourceRepository<DataSourceTinyDto>
|
||||
{
|
||||
private readonly DbDataManager _dataManager;
|
||||
private readonly ILogger<DbDataManager> _logger;
|
||||
|
||||
public DataSourceRepository(DbDataManager dbDataManager, ILogger<DbDataManager> logger)
|
||||
{
|
||||
_dataManager = dbDataManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public async Task<DataSourceTinyDto?> GetItemById(int id)
|
||||
{
|
||||
var dataSource = await _dataManager.DbContext.DataSourcesSet.FindAsync(id);
|
||||
if (dataSource == null)
|
||||
{
|
||||
_logger.LogInformation($"DataSource with ID {id} not found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return dataSource.ToTinyDto();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
namespace Shared;
|
||||
|
||||
public class FriendShipException : ModelNotFoundException
|
||||
{
|
||||
public FriendShipException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ModelNotFoundException : Exception
|
||||
{
|
||||
public ModelNotFoundException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
using Xunit;
|
||||
using Model2Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DbContextLib;
|
||||
using StubbedContextLib;
|
||||
using System.Linq;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System;
|
||||
using EFMappers;
|
||||
using Shared;
|
||||
using Model;
|
||||
using Moq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Entities;
|
||||
/*
|
||||
namespace UnitTestsEntities
|
||||
{
|
||||
public class ActivityRepositoryTests : IClassFixture<DatabaseFixture>
|
||||
{
|
||||
private readonly DatabaseFixture _fixture;
|
||||
|
||||
public ActivityRepositoryTests(DatabaseFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetActivities_ReturnsActivities()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<HeartTrackContext>()
|
||||
.UseSqlite(_fixture._connection)
|
||||
.Options;
|
||||
|
||||
using (var context = new HeartTrackContext(options))
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
using (var context = new HeartTrackContext(options))
|
||||
{
|
||||
var repository = new DbDataManager.ActivityRepository(new DbDataManager(context), null);
|
||||
var activities = await repository.GetActivities(0, 10, ActivityOrderCriteria.None);
|
||||
|
||||
Assert.NotNull(activities);
|
||||
Assert.Equal(10, activities.Count());
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public async Task GetActivityByIdAsync_ReturnsCorrectActivity_WhenIdExists()
|
||||
{
|
||||
// Arrange
|
||||
var activityId = 1;
|
||||
var expectedActivity = new Activity { Id = activityId, Type = "Running" };
|
||||
|
||||
var mockDataManager = new Mock<DbDataManager>();
|
||||
mockDataManager.Setup(dm => dm.DbContext.ActivitiesSet.SingleOrDefaultAsync(a => a.IdActivity == activityId))
|
||||
.ReturnsAsync(expectedActivity.ToEntity());
|
||||
|
||||
var loggerMock = new Mock<ILogger<DbDataManager>>();
|
||||
var activityRepository = new DbDataManager.ActivityRepository(mockDataManager.Object, loggerMock.Object);
|
||||
|
||||
// Act
|
||||
var result = await activityRepository.GetActivityByIdAsync(activityId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(expectedActivity.Id, result.Id);
|
||||
Assert.Equal(expectedActivity.Type, result.Type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetActivityByIdAsync_ReturnsNull_WhenIdDoesNotExist()
|
||||
{
|
||||
// Arrange
|
||||
var activityId = 999;
|
||||
|
||||
var mockDataManager = new Mock<DbDataManager>();
|
||||
mockDataManager.Setup(dm => dm.DbContext.ActivitiesSet.SingleOrDefaultAsync(a => a.IdActivity == activityId))
|
||||
.ReturnsAsync((ActivityEntity)null);
|
||||
|
||||
var loggerMock = new Mock<ILogger<DbDataManager>>();
|
||||
var activityRepository = new DbDataManager.ActivityRepository(mockDataManager.Object, loggerMock.Object);
|
||||
|
||||
// Act
|
||||
var result = await activityRepository.GetActivityByIdAsync(activityId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddActivity_SuccessfullyAddsNewActivity()
|
||||
{
|
||||
// Arrange
|
||||
var newActivity = new Activity { Type = "Walking" };
|
||||
|
||||
var mockDataManager = new Mock<DbDataManager>();
|
||||
mockDataManager.Setup(dm => dm.DbContext.AddItem(It.IsAny<ActivityEntity>()))
|
||||
.ReturnsAsync(newActivity.ToEntity());
|
||||
|
||||
var loggerMock = new Mock<ILogger<DbDataManager>>();
|
||||
var activityRepository = new DbDataManager.ActivityRepository(mockDataManager.Object, loggerMock.Object);
|
||||
|
||||
// Act
|
||||
var result = await activityRepository.AddActivity(newActivity);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(newActivity.Type, result.Type);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
@ -0,0 +1 @@
|
||||
global using Xunit;
|
@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Model2Entities\Model2Entities.csproj" />
|
||||
<ProjectReference Include="..\UnitTestsEntities\UnitTestsEntities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,10 @@
|
||||
namespace RepositoriesUnitTest;
|
||||
|
||||
public class UnitTest1
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
global using Microsoft.VisualStudio.TestTools.UnitTesting;
|
@ -1,26 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4"/>
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.0.4"/>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\HeartTrackAPI\HeartTrackAPI.csproj" />
|
||||
<ProjectReference Include="..\..\Model\Model.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||
<ProjectReference Include="..\..\StubAPI\StubAPI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,12 +0,0 @@
|
||||
namespace ClientTests;
|
||||
|
||||
public class HttpClientManager
|
||||
{
|
||||
protected readonly HttpClient _httpClient;
|
||||
|
||||
public HttpClientManager(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_httpClient.BaseAddress = new Uri("https://localhost:7252");
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue