Compare commits

..

No commits in common. 'master' and 'Test_CI' have entirely different histories.

@ -2,11 +2,10 @@ kind: pipeline
type: docker type: docker
name: HeartTrack-API name: HeartTrack-API
#trigger: trigger:
# branch: event:
# - master - push
# event:
# - push
steps: steps:
- name: build - name: build
image: mcr.microsoft.com/dotnet/sdk:8.0 image: mcr.microsoft.com/dotnet/sdk:8.0
@ -32,12 +31,11 @@ steps:
sonar_token: sonar_token:
from_secret: SECRET_SONAR_LOGIN from_secret: SECRET_SONAR_LOGIN
project_key: HeartTrack-API project_key: HeartTrack-API
coverage_exclusions: Tests/**, StubbedContextLib/**, StubAPI/** coverage_exclusions: Tests/**, StubbedContextLib/**
duplication_exclusions: Tests/**, StubbedContextLib/**
commands: commands:
- cd src/ - cd src/
- dotnet restore HeartTrack.sln - dotnet restore HeartTrack.sln
- dotnet sonarscanner begin /k:HeartTrack-API /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.login=$${PLUGIN_SONAR_TOKEN} /d:sonar.coverage.exclusions="Tests/**, StubbedContextLib/**, StubAPI/**, HeartTrackAPI/Utils/**" /d:sonar.cpd.exclusions="Tests/**, StubbedContextLib/**, StubAPI/**" /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml" - dotnet sonarscanner begin /k:HeartTrack-API /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.login=$${PLUGIN_SONAR_TOKEN} /d:sonar.coverage.exclusions="Tests/**"
- dotnet build HeartTrack.sln -c Release --no-restore - dotnet build HeartTrack.sln -c Release --no-restore
- dotnet test HeartTrack.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage" - dotnet test HeartTrack.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage"
- reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport" - reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport"
@ -46,7 +44,7 @@ steps:
depends_on: [ tests ] depends_on: [ tests ]
- name: swagger - name: swagger
image: mcr.microsoft.com/dotnet/sdk:7.0 image: mcr.microsoft.com/dotnet/sdk:8.0
failure: ignore failure: ignore
volumes: volumes:
- name: docs - name: docs
@ -83,6 +81,7 @@ volumes:
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: HeartTrack-API-CD name: HeartTrack-API-CD
@ -128,6 +127,8 @@ steps:
# OVERWRITE: true # OVERWRITE: true
# depends_on: [ docker-build-and-push, deploy-container-stub ] # depends_on: [ docker-build-and-push, deploy-container-stub ]
# database container deployment # database container deployment
- name: deploy-container-mysql - name: deploy-container-mysql
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
@ -136,7 +137,7 @@ steps:
CONTAINERNAME: mysql CONTAINERNAME: mysql
COMMAND: create COMMAND: create
OVERWRITE: true OVERWRITE: true
PRIVATE: true PRIVATE: false
CODEFIRST_CLIENTDRONE_ENV_MARIADB_ROOT_PASSWORD: CODEFIRST_CLIENTDRONE_ENV_MARIADB_ROOT_PASSWORD:
from_secret: db_root_password from_secret: db_root_password
CODEFIRST_CLIENTDRONE_ENV_MARIADB_DATABASE: CODEFIRST_CLIENTDRONE_ENV_MARIADB_DATABASE:
@ -164,25 +165,7 @@ steps:
IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest
CONTAINERNAME: api CONTAINERNAME: api
CODEFIRST_CLIENTDRONE_ENV_PORT: 8080 CODEFIRST_CLIENTDRONE_ENV_PORT: 8080
ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond,camillepetitalot
COMMAND: create COMMAND: create
OVERWRITE: true OVERWRITE: true
depends_on: [deploy-container-mysql, docker-build-and-push, deploy-container-stub] depends_on: [deploy-container-mysql, docker-build-and-push, deploy-container-stub]
- name: deploy-container-phpmyadmin
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: phpmyadmin/phpmyadmin
CONTAINERNAME: phpmyadmin
COMMAND: create
OVERWRITE: true
CODEFIRST_CLIENTDRONE_ENV_PMA_HOST: HeartDev-mysql
CODEFIRST_CLIENTDRONE_ENV_PMA_PORT: 3306
# CODEFIRST_CLIENTDRONE_ENV_PMA_ABSOLUTE_URI: /containers/HeartDev-phpmyadmin
CODEFIRST_CLIENTDRONE_ENV_PMA_USER:
from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_PMA_PASSWORD:
from_secret: db_password
# CODEFIRST_CLIENTDRONE_ENV_PMA_ABSOLUTE_URI: /phpmyadmin
ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond
depends_on: [deploy-container-mysql]

@ -1,167 +1,3 @@
<div align = center> # EF_WebAPI
<h1>HeartTrack</h1> This repository make a meeting of EF and WebAPI parts.
<img src="docs/images/logo.png" />
</div>
<div align = center>
---
&nbsp; ![C#](https://img.shields.io/badge/C%23-000?style=for-the-badge&logo=c-sharp&logoColor=white&color=purple)
&nbsp; ![Entity Framework](https://img.shields.io/badge/Entity_Framework-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)
&nbsp; ![API](https://img.shields.io/badge/API-000?style=for-the-badge&logo=api&logoColor=white&color=orange)
</br>
[![Build Status](https://codefirst.iut.uca.fr/api/badges/HeartDev/API/status.svg)](https://codefirst.iut.uca.fr/HeartDev/API)
[![Bugs](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=bugs&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
[![Coverage](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=coverage&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
[![Duplicated Lines (%)](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=duplicated_lines_density&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
[![Maintainability Rating](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=sqale_rating&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
[![Reliability Rating](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=reliability_rating&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
[![Security Rating](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=security_rating&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
[![Vulnerabilities](https://codefirst.iut.uca.fr/sonar/api/project_badges/measure?project=HeartTrack-API&metric=vulnerabilities&token=c3e0444eb978e1d346ed0041c552b24870316a03)](https://codefirst.iut.uca.fr/sonar/dashboard?id=HeartTrack-API)
</div>
# Table des matières
[Présentation](#présentation) | [Répartition du Git](#répartition-du-git) | [Documentation](#documentation) | [Prerequisites](#prerequisites) | [Getting Started](#getting-started) | [Features](#features) | [Ce que nous avons fait](#ce-que-nous-avons-fait) | [Fabriqué avec](#fabriqué-avec) | [Contributeurs](#contributeurs) | [Comment contribuer](#comment-contribuer) | [License](#license) | [Remerciements](#remerciements)
## Présentation
**Nom de l'application :** HeartTrack
### Contexte
HeartTrack est une application web PHP et mobile Android destinée aux sportifs et aux coachs afin de permettre l'analyse de courbes de fréquences cardiaques et le suivi d'équipe sportive. L'objectif principal de cette application est de récupérer les données de fréquence cardiaque à partir de fichiers .FIT, de les afficher sous forme de courbes, d'identifier des paternes, de fournir des statistiques et de réaliser des prédictions liées à l'effort physique, à la chaleur, à la récupération, etc.
!! Fuseaux horaires de base
### Récapitulatif du Projet
Le projet HeartTrack, avec son application HeartTrack, vise à offrir une solution Open Source d'analyse des données de fréquence cardiaque, en mettant l'accent sur les besoins des sportifs et des coachs. L'application sera capable de traiter et d'interpréter les données de manière intelligente, fournissant ainsi des informations précieuses pour optimiser les performances sportives et la santé.
## Répartition du Git
[**Sources**](Sources/) : **Code de l'application**
[**Documents**](docs/Diagramme/README_DIAGRAMMES.md) : **Documentation de l'application et diagrammes**
[**Wiki**](https://codefirst.iut.uca.fr/git/HeartDev/Web/wiki/PHP) : **Wiki de notre projet (attendus PHP)**
---
Le projet HeartTrack utilise un modèle de flux de travail Git (Gitflow) pour organiser le développement. Voici une brève explication des principales branches :
- **branche WORK-** : Cette branche est utilisée pour le développement de nouvelles fonctionnalités. Chaque fonctionnalité est développée dans une branche séparée. WORK-NOMDUDEV permet de savoir qui travaille sur quoi.
- **branche master** : Cette branch est la dernière version stable de l'application. Les modifications sur cette branche sont généralement destinées à des mises en production.
- **branche test** : Cette branche est utilisée pour tester les différentes fonctionnalités avant de les fusionner.
- **branche merge** : Cette branche est utilisée pour fusionner les différentes branches de fonctionnalités.
## Documentation
Documentation et informations à propos de `HearthTrack` disponible [ici](https://codefirst.iut.uca.fr/documentation/HeartDev/API/doxygen/)
### Prerequisites
* [![.NET 8.0](https://img.shields.io/badge/Langage-.NET-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)](https://dotnet.microsoft.com/download/dotnet/8.0)
* [![Entity Framework](https://img.shields.io/badge/ORM-Entity_Framework-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)](https://docs.microsoft.com/fr-fr/ef/)
* [![API](https://img.shields.io/badge/API-000?style=for-the-badge&logo=api&logoColor=white&color=orange)](https://docs.microsoft.com/fr-fr/aspnet/core/web-api/?view=aspnetcore-8.0)
* [![Visual Studio](https://img.shields.io/badge/IDE-Visual_Studio-000?style=for-the-badge&logo=visual-studio&logoColor=white&color=purple)](https://visualstudio.microsoft.com/fr/)
* [![Git](https://img.shields.io/badge/Versioning-Git-000?style=for-the-badge&logo=git&logoColor=white&color=red)](https://git-scm.com/)
## Getting Started
## Ce que nous avons fait
### Entity Framework
réalisé | niveau | description | coeff | jalon
--- | --- | --- | --- | ---
✅ | ☢️ | Le dépôt doit être accessible par l'enseignant | ☢️ | J1
✅ | ☢️ | un .gitignore doit exister au premier push | ☢️ | J1
✅ | 🎬 | les *projets* et les tests compilent | 1 | J1 & J2
✅ | 🎬 | le projet et le tests s'exécutent sans bug (concernant la partie persistance) | 3 | J1 & J2
✅ | 🟢 | Transcription du modèle : Modèle vers entités (et inversement) | 2 | J1
✅ | 🟢 | Requêtes CRUD simples (sur une table) | 1 | J1
✅ | 🟢 | Utilisation de LINQ to Entities | 2 | J1
✅ | 🟡 | Injection / indépendance du fournisseur | 1 | J1
✅ | 🟡 | Requêtes CRUD sur des données complexes (images par exemple) | 2 | J1
✅ | 🟢 | Tests - Appli Console | 1 | J1
✅ | 🟢 | Tests - Tests unitaires (avec SQLite in memory) | 2 | J1
✅ | 🟢 | Tests - Données stubbées et/ou Moq | 1 | J1
✅ | 🟡 | CI : build, tests, Sonar (doc?) | 1 | J1
✅ | 🟡 | Utilisation de relations (One-to-One, One-to-Many, Many-to-Many) (+ mapping, TU, Requêtes) | 4 | J1
✅ | 🟢 | Liens avec le web service | 2 | J1
✅ | 🟡 | Utilisation d'un *Logger* | 1 | J1
✅ | 🟡 | Déploiement | 4 | J2
✅ | 🔴 | Unit of Work / Repository + extras (héritage, accès concurrents...) | 8 | J2
✅ | 🟢 | Utilisation dans le projet | 2 | J2
✅ | 🟢 | mon dépôt possède un readme qui apporte quelque chose... | 2 | J2
### API
réalisé | niveau | description | coeff | jalon
--- | --- | --- | --- | ---
✅ | ☢️ | Le dépôt doit être accessible par l'enseignant | ☢️ | J1
✅ | ☢️ | un .gitignore doit exister au premier push | ☢️ | J1
✅ | 🎬 | les *projets* et les tests compilent | 1 | J1 & J2
✅ | 🎬 | le projet et le tests s'exécutent sans bug (concernant la partie persistance) | 4 | J1 & J2
✅ | 🟢 | Modèle <-> DTO | 1 | J1
✅ | 🟢 | Entities <-> DTO | 1 | J1
✅ | 🟡 | Authentification | 4 | J1
✅ | 🟢 | Requêtes GET, PUT, POST, DELETE sur des données simples (1 seul type d'objet en retour, propriétés de types natifs) | 2 | J1
✅ | 🟡 | Pagination & filtrage | 2 | J1
✅ | 🟢 | Injection de service | 2 | J1
✅ | 🟡 | Requêtes GET, PUT, POST, DELETE sur des données complexes (plusieurs données complexes en retour) | 4 | J1
✅ | 🟢 | Tests - Appli Console (consommation des requêtes) | 4 | J1
✅ | 🟢 | Tests - Tests unitaires (avec Stub et/ou Moq) | 2 | J1
✅ | 🟡 | CI : build, tests, Sonar, Documentation (en particulier Swagger avec exemples...) | 1 | J1
✅ | 🟢 | Liens avec la persistance en base de données | 4 | J1
✅ | 🟡 | Utilisation d'un *Logger* | 1 | J1
✅ | 🟡 | Déploiement | 4 | J2
✅ | 🟡 | Utilisation dans le projet | 4 | J2
✅ | 🎬 | mon dépôt possède un readme qui apporte quelque chose... | 1 | J2
## Fabriqué avec
![.NET](https://img.shields.io/badge/Langage-.NET-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)
![Entity Framework](https://img.shields.io/badge/ORM-Entity_Framework-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)
![API](https://img.shields.io/badge/API-000?style=for-the-badge&logo=api&logoColor=white&color=orange)
![ASP.NET](https://img.shields.io/badge/ASP.NET-000?style=for-the-badge&logo=asp.net&logoColor=white&color=blue)
![Visual Studio](https://img.shields.io/badge/IDE-Visual_Studio-000?style=for-the-badge&logo=visual-studio&logoColor=white&color=purple)
![JetBrains Rider](https://img.shields.io/badge/IDE-JetBrains_Rider-000?style=for-the-badge&logo=rider&logoColor=white&color=purple)
![Git](https://img.shields.io/badge/Versioning-Git-000?style=for-the-badge&logo=git&logoColor=white&color=red)
![SonarQube](https://img.shields.io/badge/Qualit%C3%A9-SonarQube-000?style=for-the-badge&logo=sonarqube&logoColor=white&color=red)
![Drone](https://img.shields.io/badge/CI-Drone-000?style=for-the-badge&logo=drone&logoColor=white&color=orange)
![Docker](https://img.shields.io/badge/Container-Docker-000?style=for-the-badge&logo=docker&logoColor=white&color=blue)
![C#](https://img.shields.io/badge/Langage-C%23-000?style=for-the-badge&logo=c-sharp&logoColor=white&color=purple)
![Doxygen](https://img.shields.io/badge/Documentation-Doxygen-000?style=for-the-badge&logo=doxygen&logoColor=white&color=blue)
## Contributeurs
* [Antoine PEREDERII](https://codefirst.iut.uca.fr/git/antoine.perederii)
* [Paul LEVRAULT](https://codefirst.iut.uca.fr/git/paul.levrault)
* [Kevin MONTEIRO](https://codefirst.iut.uca.fr/git/kevin.monteiro)
* [Antoine PINAGOT](https://codefirst.iut.uca.fr/git/antoine.pinagot)
* [David D'HALMEIDA](https://codefirst.iut.uca.fr/git/david.d_almeida)
## Comment contribuer
1. Forkez le projet (<https://codefirst.iut.uca.fr/git/HeartDev/API>)
2. Créez votre branche (`git checkout -b feature/featureName`)
3. commit vos changements (`git commit -am 'Add some feature'`)
4. Push sur la branche (`git push origin feature/featureName`)
5. Créez une nouvelle Pull Request
## License
Ce projet est sous licence ``MIT`` - voir le fichier [LICENSE.md](LICENSE.md) pour plus d'informations.
## Remerciements
Ce projet a été réalisé dans le cadre de la SAÉ de l'IUT de Clermont-Ferrand.

@ -1,113 +0,0 @@
@startuml
skinparam classAttributeIconSize 0
package MLD{
entity "Athlète" as athlete {
{static} idAthlete
nom
prénom
email
sexe
taille
poids
motDePasse
dateNaissance
}
entity "Amitié" as friendship{
{static}# idAthlete1
{static}# idAthlete2
début
}
entity "Notification" as notif {
{static} idNotif
message
date
statut
urgence
#athleteId
}
entity "Coach" as coach {
{static} idCoach
// attributs spécifiques au coach
#athleteId
}
entity "Statistique" as stats {
{static} idStatistique
poids
fcMoyenne
fcMax
caloriesBrûléesMoy
date
#athleteId
}
entity "Entraînement" as training {
{static} idEntrainement
date
description
// Exercices
latitude
longitude
feedback
#coachId
}
entity "Participe" as takepart {
{static} #athleteId
{static} #entrainementId
}
entity "SourceDonnée" as source {
{static} idSource
type
modèle
précision
#athleteId
}
entity "Activité" as activity {
{static} idActivité
type
date
heureDeDébut
heureDeFin
effortRessent
variabilité
variance
ecartType
moyenne
maximum
minimum
temperatureMoyenne
#athleteId
#sourceId
}
entity "FréquenceCardiaque" as fc {
{static} idFc
altitude
temps : time
température
bpm
longitude
latitude
#activitéId
}
}
activity --> athlete
activity --> source
activity <-- fc
coach --> athlete
athlete <-- source
stats --> athlete
takepart --> athlete
takepart --> training
friendship --> athlete
notif --> athlete
coach <-- training
athlete <-- friendship
@enduml

@ -1,275 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# BDD
## Modèle Logique de Données (MLD)
Le MLD représente la structure de données de l'application, décrivant les entités et les relations entre elles. Voici un aperçu des principales entités du MLD :
### Athlète (Athlete)
L'entité principale représentant un athlète avec ces informations propre à lui telles que l'identifiant, le nom, le prénom, l'email, etc. Les athlètes peuvent être coach avec le boolean idCoach et être liés par des amitiés, ou par un coaching via la table `Amitie`.
### Amitié (Friendship)
Une entité qui modélise les relations d'amitié entre les athlètes et de coaching entre les athlètes et les coachs. Elle stocke les identifiants des deux utilisateurs impliqués.
### Notification (Notification)
L'entité qui stocke les notifications destinées aux athlètes, avec des détails tels que le message, la date, le statut, et le degré d'urgence.
### Envoi de Notification (SendNotification)
Une entité de liaison entre les athlètes et les notifications, indiquant quel athlète ou coach a envoyé quelle notification. Cela peut-être utile lors d'une notification d'ajout d'amie par exemple.
### Statistique (Statistic)
Les statistiques relatives à un athlètes, y compris le poids, la fréquence cardiaque moyenne, la fréquence cardiaque maximale, etc.
### Entraînement (Training)
Détails sur les sessions d'entraînement planifiés par un coach pour ses athlètes, comprenant la date, la description, la localisation, etc. Les athlètes peuvent participer à des entraînements et donner leur feedback sur l'entrainement donné.
### Participation (Participate)
Une entité de liaison entre les athlètes et les entraînements, indiquant quels athlètes participent à quels entraînements.
### Don (GiveParticipation)
Une entité de liaison entre les coachs et les entraînements, indiquant quels coachs ont attribué quels entraînements.
### Source de Données (DataSource)
L'entité représentant la source des données des enregistrements sportif, telle que le type, le modèle, la précision, etc., utilisée par les athlètes pour enregistrer une ou des activités.
### Activité (Activity)
Les détails des activités des athlètes, y compris le type, la date, les heures de début et de fin, l'effort ressenti, etc.
### Fréquence Cardiaque (HeartRate)
Les données de fréquence cardiaque enregistrées pendant les activités, avec des informations telles que l'altitude, la température, etc.
Ce MLD forme la base de données sous-jacente pour l'application, offrant une structure organisée pour stocker et récupérer les informations relatives aux athlètes et à leurs activités.
```plantuml
@startuml
skinparam classAttributeIconSize 0
package MLD{
entity "Athlete" as athlete {
{static} idAthlete
username
nom
prenom
email
sexe
taille
poids
motDePasse
dateNaissance
isCoach
}
entity "Amitie" as friendship{
{static}# idAthlete1
{static}# idAthlete2
début
}
entity "Notification" as notif {
{static} idNotif
message
date
statut
urgence
#athleteId
}
entity "Envoi" as sendNotif{
{static}# idAthlete
{static}# idNotif
}
entity "Statistique" as stats {
{static} idStatistique
poids
fcMoyenne
fcMax
caloriesBruleesMoy
date
#athleteId
}
entity "Entrainement" as training {
{static} idEntrainement
date
description
latitude
longitude
feedback
#athleteId
}
entity "Participe" as takepart {
{static} #athleteId
{static} #entrainementId
}
entity "Donne" as givepart {
{static} #coachId
{static} #entrainementId
}
entity "SourceDonnee" as source {
{static} idSource
type
modele
precision
#athleteId
}
entity "Activite" as activity {
{static} idActivité
type
date
heureDeDebut
heureDeFin
effortRessent
variabilite
variance
ecartType
moyenne
maximum
minimum
temperatureMoyenne
#athleteId
#sourceId
}
entity "FréquenceCardiaque" as fc {
{static} idFc
altitude
temps : time
température
bpm
longitude
latitude
#activitéId
}
}
activity --> athlete
activity --> source
activity <-- fc
athlete <-- source
stats --> athlete
takepart --> athlete
takepart --> training
givepart --> athlete
givepart --> training
sendNotif --> athlete
sendNotif --> notif
friendship --> athlete
notif --> athlete
athlete <-- friendship
@enduml
```
```plantuml
@startuml
skinparam classAttributeIconSize 0
package MCD{
entity "Athlete" as athlete {
{static} idAthlete
username
nom
prenom
email
sexe
taille
poids
motDePasse
dateNaissance
isCoach
}
entity "Notification" as notif {
{static} idNotif
message
date
statut
urgence
#athleteId
}
entity "Statistique" as stats {
{static} idStatistique
poids
fcMoyenne
fcMax
caloriesBruleesMoy
date
#athleteId
}
entity "Entrainement" as training {
{static} idEntrainement
date
description
latitude
longitude
feedback
#athleteId
}
entity "SourceDonnee" as source {
{static} idSource
type
modele
precision
#athleteId
}
entity "Activite" as activity {
{static} idActivité
type
date
heureDeDebut
heureDeFin
effortRessent
variabilite
variance
ecartType
moyenne
maximum
minimum
temperatureMoyenne
#athleteId
#sourceId
}
entity "FréquenceCardiaque" as fc {
{static} idFc
altitude
temps : time
température
bpm
longitude
latitude
#activitéId
}
}
activity "0..n" --- "1..1" athlete : réalise
activity "1..n" --- "1..1" source : possede
activity "1..1" --- "1..n" fc : enregistre
athlete "1..n" --- "0..1" source : possede
stats "0..n" --- "1..1" athlete : possede
training "0..n" --- "1..n" athlete : participe
training "0..n" --- "1..1" athlete : donne
athlete "0..n" --- "1..n" athlete : est ami
notif "0..n" --- "1..n" athlete : recoit
notif "0..n" --- "1..1" athlete : envoie
@enduml
```

@ -1,55 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Cas d'utilisation : Suivi d'une Équipe Sportive
Bienvenue dans le monde dynamique du suivi d'équipe sportive, où notre application offre une plateforme complète pour les entraîneurs soucieux d'optimiser les performances de leurs athlètes. Ce diagramme de cas d'utilisation vous plonge dans les fonctionnalités clés qui facilitent la gestion d'une équipe sportive avec efficacité.
**Acteurs Principaux :**
- **Coach :** Le protagoniste central, utilisant l'application pour gérer et superviser son équipe.
**Fonctionnalités Clés :**
- **Ajouter un Athlète :** Permet au coach d'ajouter de nouveaux membres à son équipe, avec des étapes incluant la validation par l'athlète et l'authentification.
- **Supprimer un Athlète :** Offre la possibilité de retirer des athlètes de l'équipe, avec une authentification préalable pour garantir la légitimité de l'action.
- **Afficher ses Athlètes :** Permet au coach de visualiser la liste complète de ses athlètes, nécessitant une authentification pour accéder à ces informations sensibles.
- **Afficher les Activités de Tous les Athlètes :** Donne au coach un aperçu global des activités de toute l'équipe, nécessitant une authentification pour garantir la confidentialité des données.
**Flux d'Interaction :**
- Le processus d'ajout d'un athlète inclut des étapes telles que la validation par l'athlète et l'authentification, garantissant une intégration fluide.
- Les actions de suppression, affichage des athlètes et affichage des activités nécessitent une authentification préalable pour assurer la sécurité des données.
- Des extensions telles que la visualisation des activités d'un athlète et l'analyse des performances offrent des fonctionnalités avancées pour un suivi détaillé.
Explorez ce diagramme pour comprendre l'étendue des fonctionnalités que notre application offre aux entraîneurs, les aidant à gérer leurs équipes de manière efficace et à maximiser le potentiel de chaque athlète.
```plantuml
left to right direction
:Coach: as a
a --> (Ajouter un athlète)
a --> (Supprimer un athlète)
a --> (Afficher ses athlètes )
a --> (Afficher les activités de tous les athlètes)
(Ajouter un athlète).>(Validation par l'athlète) : <<include>>
(Ajouter un athlète)..>(S'authentifier) : <<include>>
(Supprimer un athlète)..>(S'authentifier) : <<include>>
(Afficher ses athlètes )..>(S'authentifier) : <<include>>
(Afficher les activités de tous les athlètes)..>(S'authentifier) : <<include>>
(S'authentifier)..>(S'inscrire) : <<extends>>
(S'inscrire).>(Inscription Coach) : <<include>>
(S'authentifier)..>(Se connecter) : <<include>>
(Afficher ses athlètes )..>(Voir les activités d'un athlète) : <<extends>>
(Afficher ses athlètes )..>(Voir les stats d'un athlète) : <<extends>>
(Afficher les activités de tous les athlètes)..>(Sélectionner une activité) : <<include>>
(Sélectionner une activité)..>(Voir l'analyse) : <<extends>>
(Sélectionner une activité)..>(Exporter l'analyse) : <<extends>>
(Voir les activités d'un athlète)..>(Voir l'analyse) : <<extends>>
(Voir les activités d'un athlète)..>(Exporter l'analyse) : <<extends>>
```

@ -1,57 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Cas d'utilisation : Gestion d'Activités pour un Athlète
Bienvenue dans l'univers dédié à la gestion d'activités sportives personnalisées pour les athlètes ! Ce diagramme de cas d'utilisation explore les différentes fonctionnalités offertes aux utilisateurs, mettant en avant la flexibilité et la richesse d'interactions pour une expérience utilisateur optimale.
**Acteurs Principaux :**
- **Athlète :** Le protagoniste central, utilisant l'application pour importer, gérer et analyser ses activités sportives.
**Fonctionnalités Clés :**
- **Importer des Données :** Permet à l'athlète d'importer des données d'activités depuis différentes sources, avec la possibilité de spécifier la source pour une intégration transparente.
- **Exporter Mes Données :** Offre la possibilité d'exporter l'ensemble des activités, avec des extensions pour exporter une activité spécifique, le tout soumis à une authentification préalable.
- **Ajouter une Activité :** Permet à l'athlète d'ajouter de nouvelles activités, avec des étapes inclusives telles que la saisie du titre, du type d'activité, de la source, du matériel utilisé et de la visibilité, chacune accessible via l'authentification.
- **Voir une Activité :** Permet à l'athlète de visualiser en détail une activité particulière, avec la possibilité d'exporter une analyse et de gérer la visibilité, soumis à une authentification.
- **Supprimer une Activité :** Offre la possibilité de retirer une activité, requérant une authentification pour garantir la sécurité des données.
**Flux d'Interaction :**
- Les actions telles que l'importation, l'exportation, l'ajout et la visualisation d'activités impliquent une authentification préalable pour garantir la confidentialité des données personnelles.
- Des inclusions précises, telles que la saisie du titre, du type d'activité, de la source, du matériel utilisé et de la visibilité, sont incorporées dans le processus d'ajout d'une activité, offrant une expérience utilisateur détaillée et conviviale.
Explorez ce diagramme pour comprendre la manière dont notre application place la gestion d'activités entre les mains des athlètes, les encourageant à suivre, analyser et optimiser leurs performances sportives de manière personnalisée et efficace.
```plantuml
left to right direction
:Athlete: as a
a --> (Importer des données)
(Importer des données) .> (Saisir la source) : <<include>>
a --> (Exporter mes données)
(Exporter mes données) .>(Exporter toutes les activités): <<extends>>
(Exporter mes données) ..>(Exporter une activité): <<include>>
a --> (Ajouter une activité)
(Ajouter une activité) ..>(Saisir un titre et une description): <<include>>
(Ajouter une activité) ..>(Saisir le type d'activité): <<include>>
(Ajouter une activité) .>(Saisir la source): <<include>>
(Saisir la source) ..>(Saisir le matériel utilisé): <<include>>
(Ajouter une activité) ..>(Saisir la visibilité): <<include>>
a --> (Voir une activité)
(Voir une activité) ..>(Exporter l'analyse): <<extends>>
(Voir une activité) ..>(Saisir la visibilité): <<extends>>
a --> (Supprimer une activité)
(Supprimer une activité) ..>(S'authentifier): <<include>>
(Importer des données) ...>(S'authentifier): <<include>>
(Exporter mes données) ...>(S'authentifier): <<include>>
(Ajouter une activité) ...>(S'authentifier): <<include>>
(Voir une activité) ...>(S'authentifier): <<include>>
```

@ -1,55 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Cas d'utilisation : Gestion des Relations Sociales pour un Athlète
Bienvenue dans la sphère sociale de notre application dédiée aux athlètes ! Ce diagramme de cas d'utilisation explore les fonctionnalités sociales clés offertes aux utilisateurs, mettant en lumière la connectivité et l'interaction sociale au sein de notre communauté sportive.
**Acteurs Principaux :**
- **Athlète :** Le protagoniste central, utilisant l'application pour gérer ses relations sociales et explorer les profils de ses pairs.
**Fonctionnalités Clés :**
- **Ajouter un Ami :** Permet à l'athlète d'ajouter de nouveaux amis, nécessitant la saisie du nom de l'ami et soumis à une authentification préalable.
- **Supprimer un Ami :** Offre la possibilité de retirer un ami, exigeant une authentification pour garantir la sécurité des données.
- **Voir Mes Amis :** Permet à l'athlète de visualiser la liste de ses amis, avec la possibilité d'accéder à des fonctionnalités supplémentaires comme la visualisation des profils.
- **Modifier Mes Informations :** Offre à l'athlète la possibilité de mettre à jour ses informations personnelles et de connexion, avec des extensions pour des détails plus spécifiques.
**Flux d'Interaction :**
- Le processus d'ajout d'un ami inclut la saisie du nom de l'ami, tandis que la suppression d'un ami et la visualisation de la liste des amis sont soumises à une authentification préalable pour protéger la confidentialité.
- Les modifications d'informations englobent deux extensions : la mise à jour des informations personnelles et la mise à jour des informations de connexion, offrant une personnalisation approfondie du profil athlétique.
- La visualisation du profil d'un ami s'étend à des fonctionnalités telles que la consultation des activités et des statistiques de l'ami, ajoutant une dimension sociale à l'expérience de suivi sportif.
Explorez ce diagramme pour découvrir comment notre application encourage l'interaction sociale entre les athlètes, favorisant une communauté engagée et collaborative au sein de laquelle les utilisateurs peuvent partager, interagir et se soutenir mutuellement dans leur parcours sportif.
```plantuml
left to right direction
:Athlete: as a
a --> (Ajouter un ami)
a --> (Supprimer un ami)
a --> (Voir mes amis)
a --> (Modifier mes informations)
(Ajouter un ami)->(Saisir le nom de l'ami)
(Supprimer un ami)..>(S'authentifier) : <<include>>
(Ajouter un ami)..>(S'authentifier) : <<include>>
(Voir mes amis)..>(S'authentifier) : <<include>>
(Voir mes amis)..>(Lister les amis) : <<include>>
(Modifier mes informations)..>(Informations personnelles) : <<extends>>
(Modifier mes informations)..>(Informations de connexion) : <<extends>>
(Modifier mes informations)..>(S'authentifier) : <<include>>
(Lister les amis)..>(Voir son profil) : <<include>>
(Voir son profil)..>(Voir ses activités) : <<extends>>
(Voir son profil)..>(Voir ses statistiques) : <<extends>>
(S'authentifier)..>(S'inscrire) : <<extends>>
(S'authentifier)..>(Se connecter) : <<include>>
(S'inscrire)..>(Inscription Athlète) : <<include>>
```

File diff suppressed because it is too large Load Diff

@ -1,202 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Diagramme de Classes : Plateforme de Gestion d'Activités Sportives
Bienvenue dans l'écosystème dynamique de notre plateforme de gestion d'activités sportives ! Ce diagramme de classes offre une vision complète des entités et des relations qui façonnent l'expérience des utilisateurs au sein de notre système.
**Entités Principales :**
- **Utilisateur (User) :** Représente les individus inscrits sur notre plateforme, avec des détails personnels tels que le nom, le prénom, l'email, etc. Chaque utilisateur a un rôle spécifique (Athlete, Coach) qui détermine ses interactions au sein de l'application.
- **Rôle (Role) :** Classe abstraite qui définit les rôles spécifiques des utilisateurs (Athlete, Coach). Contient des méthodes pour gérer les amis, les entraînements, et les demandes.
- **Athlète (Athlete) :** Spécialisation de la classe Role, représentant les utilisateurs actifs qui enregistrent des activités sportives, des statistiques, et interagissent avec d'autres athlètes.
- **Activité (Activite) :** Contient des détails sur une activité sportive tels que le type, la date, la durée, la fréquence cardiaque, etc.
- **Notification (Notification) :** Messages pour informer les utilisateurs des actions importantes.
- **Entraînement (Entrainement) :** Sessions planifiées d'activités physiques avec des détails comme la date, la localisation, la description, et les retours.
- **Statistique (Statistique) :** Informations détaillées sur les performances sportives d'un athlète, comprenant la distance totale, le poids, le temps total, la fréquence cardiaque, etc.
- **Source de Données (SourceDonnees) :** Représente les sources utilisées pour collecter des données, telles que les montres connectées.
**Relations Clés :**
- Les Utilisateurs ont un rôle spécifique (Athlete, Coach) qui détermine leurs fonctionnalités.
- Un Athlète peut enregistrer plusieurs Activités, Statistiques, et interagir avec différentes Sources de Données.
- Les Entraînements sont liés aux Utilisateurs, permettant une planification efficace.
- Les Notifications informent les Utilisateurs des événements importants.
Explorez ce diagramme pour comprendre comment notre plateforme offre une expérience complète, de la gestion des utilisateurs à l'enregistrement des activités sportives et au suivi des performances.
```plantuml
@startuml
class User {
- id: int
- username: String
- nom: string
- prenom: string
- email: string
- motDePasse: string
- sexe: string
- taille: float
- poids: float
- dateNaissance: \DateTime
+ getId(): int
+ getUsername(): string
+ setUsername(string $username): void
+ setId(int $id): void
+ getNom(): string
+ setNom(string $nom): void
+ getPrenom(): string
+ setPrenom(string $prenom): void
+ getEmail(): string
+ setEmail(string $email): void
+ getMotDePasse(): string
+ setMotDePasse(string $motDePasse): void
+ getSexe(): string
+ setSexe(string $sexe): void
+ getTaille(): float
+ setTaille(float $taille): void
+ getPoids(): float
+ setPoids(float $poids): void
+ getDateNaissance(): \DateTime
+ setDateNaissance(\DateTime $dateNaissance): void
+ getRole(): Role
+ setRole(Role $role): void
+ addNotification($notification): void
+ deleteNotification($index): void
+ isValidPassword(string $password): bool
+ __toString(): string
}
abstract class Role {
- id: int
- usersRequests: array
+ getUsersList(): array
+ getUsersRequests(): array
+ addUsersRequests(RelationshipRequest $request): void
+ removeRequest(RelationshipRequest $req): bool
+ CheckAdd(User $user): bool
+ addUser(User $user): bool
+ removeUser(User $user): bool
+ addTraining(Training $training): bool
+ getTrainingsList(): array
}
abstract class Coach extends Role {
}
class CoachAthlete extends Coach {
+ CheckAdd(User $user): bool
}
class Athlete extends Role {
+ getActivities(): array
+ addActivity(Activity $myActivity): bool
+ CheckAdd(User $user): bool
}
class Activite {
- idActivity: int
- type: String
- date: \DateTime
- heureDebut: \DateTime
- heureFin: \DateTime
- effortRessenti: int
- variability: float
- variance: float
- standardDeviation: float
- average: int
- maximum: int
- minimum: int
- avrTemperature: float
- hasAutoPause: bool
+ getIdActivity(): int
+ getType(): String
+ getDate(): \DateTime
+ getHeureDebut(): \DateTime
+ getHeureFin(): \DateTime
+ getEffortRessenti(): int
+ getVariability(): float
+ getVariance(): float
+ getStandardDeviation(): float
+ getAverage(): float
+ getMaximum(): int
+ getMinimum(): int
+ getAvrTemperature(): float
+ setType(String $type): void
+ setEffortRessenti(int $effortRessenti): void
+ __toString(): String
}
class Notification {
- type: string
- message: string
- toUserId: int
+ getType(): string
+ setType(string $type): void
+ getMessage(): string
+ setMessage(string $message): void
+ getToUserId(): int
+ setToUserId(int $toUserId): void
+ __construct(int $toUserId,string $type, string $message)
+ __toString(): string
}
class Entrainement {
- idTraining: int
- date: \DateTime
- latitude: float
- longitude: float
- description: String
- feedback: String
+ getId(): int
+ getDate(): \DateTime
+ getLocation(): String
+ getDescription(): String
+ getFeedback(): String
+ __toString(): String
}
class Statistique {
- idStat: int
- distanceTotale: float
- poids: float
- tempsTotal: time
- FCmoyenne: int
- FCmin: int
- FCmax: int
- cloriesBrulees: int
+ getIdStat(): int
+ getDistanceTotale(): float
+ getPoids(): float
+ getTempsTotal(): time
+ getFCmoyenne(): int
+ getFCmin(): int
+ getFCmax(): int
+ getCloriesBrulees(): int
+ __toString(): String
}
class SourceDonnees {
- idSource: int
- nom: String
- type: String
- precision: enum
- dateDerniereUtilisation: \DateTime
+ getIdSource(): int
+ getNom(): String
+ getType(): String
+ getPrecision(): enum
+ getDateDerniereUtilisation(): \DateTime
+ __toString(): String
}
User -> Role : role
Role -> User : usersList
Athlete -> Statistique : statsList
Athlete -> Activite : activityList
Athlete -> SourceDonnees : sdList
User -> Notification : notificationList
User -> Entrainement : trainingsList
Activite -> SourceDonnees : maSource
@enduml
```

@ -1,90 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Diagramme de la Couche d'Accès aux Données
Bienvenue dans le cœur de notre système, où les données prennent vie à travers des ensembles de données (repositories) structurés et performants. Ce diagramme met en lumière la conception de la couche d'accès aux données de notre application, offrant un aperçu clair de la gestion des entités clées telles que les utilisateurs, les notifications, les demandes de relations et les entraînements.
**Principes Fondamentaux :**
- **IGenericRepository :** Une abstraction générique établissant les contrats essentiels pour l'accès aux données. Définissant des opérations standardisées telles que la récupération, la mise à jour, l'ajout et la suppression d'entités.
- **Interfaces Spécialisées :** Des interfaces telles que `IUserRepository`, `INotificationRepository`, `IRelationshipRequestRepository` et `ITrainingRepository` étendent les fonctionnalités génériques pour répondre aux besoins spécifiques de chaque entité.
**Repositories Concrets :**
- **UserRepository :** Gère les données relatives aux utilisateurs, permettant des opérations de récupération, de mise à jour et de suppression avec une efficacité optimale.
- **NotificationRepository :** Responsable de la gestion des notifications, assurant un accès structuré et une manipulation sécurisée de ces informations cruciales.
- **RelationshipRequestRepository :** Facilite la gestion des demandes de relations entre utilisateurs, garantissant une interaction claire et ordonnée.
- **TrainingRepository :** Permet l'accès et la manipulation des données liées aux entraînements, facilitant le suivi des performances athlétiques.
Explorez ce diagramme pour découvrir la robustesse de notre architecture de gestion des données, mettant en œuvre des pratiques de développement SOLID pour assurer une expérience utilisateur fiable et évolutive.
```plantuml
@startuml couche_acces_aux_donnees
abstract class IGenericRepository {
+ getItemById(int id) : object
+ getNbItems() : int
+ getItems(int index, int count, string orderingPropertyName, bool descending) : array
+ getItemsByName(string substring, int index, int count, string orderingPropertyName, bool descending) : array
+ getItemByName(string substring, int index, int count, string orderingPropertyName, bool descending) : object
+ updateItem(oldItem, newItem) : void
+ addItem(item) : void
+ deleteItem(item) : bool
}
interface IUserRepository extends IGenericRepository {
}
interface INotificationRepository extends IGenericRepository {
}
interface IRelationshipRequestRepository extends IGenericRepository {
}
interface ITrainingRepository extends IGenericRepository {
}
class NotificationRepository implements INotificationRepository {
- notifications : array
+ getItemById(int id) : object
+ getNbItems() : int
+ getItems(int index, int count, string orderingPropertyName, bool descending) : array
+ getItemsByName(string substring, int index, int count, string orderingPropertyName, bool descending) : array
+ getItemByName(string substring, int index, int count, string orderingPropertyName, bool descending) : object
+ updateItem(oldItem, newItem) : void
+ addItem(item) : void
+ deleteItem(item) : bool
}
class RelationshipRequestRepository implements IRelationshipRequestRepository {
- requests : array
+ getItemById(int id) : object
+ getNbItems() : int
+ getItems(int index, int count, string orderingPropertyName, bool descending) : array
+ getItemsByName(string substring, int index, int count, string orderingPropertyName, bool descending) : array
+ getItemByName(string substring, int index, int count, string orderingPropertyName, bool descending) : object
+ updateItem(oldItem, newItem) : void
+ addItem(item) : void
+ deleteItem(item) : bool
}
class TrainingRepository implements ITrainingRepository {
- trainings : array
+ getItemById(int id) : object
+ getNbItems() : int
+ getItems(int index, int count, string orderingPropertyName, bool descending) : array
+ getItemsByDate(date, int index, int count, string orderingPropertyName, bool descending) : array
+ updateItem(oldItem, newItem) : void
+ addItem(item) : void
+ deleteItem(item) : bool
}
class UserRepository implements IUserRepository {
- users : array
+ getItemById(int id) : object
+ getNbItems() : int
+ getItems(int index, int count, string orderingPropertyName, bool descending) : array
+ getItemsByName(string substring, int index, int count, string orderingPropertyName, bool descending) : array
+ getItemByName(string substring, int index, int count, string orderingPropertyName, bool descending) : object
+ updateItem(oldItem, newItem) : void
+ addItem(item) : void
+ deleteItem(item) : bool
}
@enduml
```

@ -1,138 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Diagramme de Classes : Statistiques pour Coach
Bienvenue dans l'univers captivant de notre système de gestion d'activités sportives avec une mise au point spéciale sur les statistiques destinées aux coaches. Ce diagramme de classes offre une vue approfondie de la manière dont les utilisateurs, en particulier les athlètes et les coaches, interagissent avec les données de performance.
**Entités Principales :**
- **Utilisateur (User) :** Représente les individus inscrits sur notre plateforme, avec des détails personnels et un rôle spécifique dans l'écosystème sportif.
- **Athlète (Athlete) :** Un type spécialisé d'utilisateur qui peut enregistrer des statistiques liées à ses activités sportives.
- **Coach (Coach) :** Un rôle qui s'étend à partir de la classe abstraite Role, dédié à la gestion des athlètes et de leurs statistiques.
- **Statistique (Statistique) :** Contient des informations détaillées sur les performances sportives d'un athlète, telles que la distance totale, le poids, le temps total, la fréquence cardiaque moyenne, minimale et maximale, ainsi que les calories brûlées.
**Relations Clés :**
- Les Utilisateurs ont un rôle spécifique (Athlete, Coach) qui influence leurs interactions au sein de la plateforme.
- Un Coach peut gérer une liste d'athlètes et avoir accès à leurs statistiques.
- Un Athlète peut enregistrer plusieurs statistiques liées à ses activités.
**Objectif Principal :**
- Permettre aux coaches d'accéder et de surveiller les statistiques détaillées de leurs athlètes, offrant ainsi un aperçu complet de leurs performances sportives.
Explorez ce diagramme pour découvrir comment notre application crée une synergie entre les utilisateurs, les rôles, et les statistiques, contribuant ainsi à une expérience enrichissante dans le suivi des activités sportives.
```plantuml
@startuml
class Athlete {
+ getAthlete(): Athlete
+ getStatistic(): ?array
+ getUsersList(): array
+ getUserList(user: User): User
+ CheckAdd(user: User): bool
+ addUser(user: User): bool
+ removeUser(user: User): bool
}
abstract class Coach {
+ abstract getUsersList(): ?array
+ abstract getUserList(user: User): User
}
class CoachAthlete {
+ getUsersList(): ?array
+ getUserList(user: User): User
}
abstract class Role {
- int id
- array usersList
- TrainingRepository trainingRepository
+ abstract __construct(trainingRepository: ?TrainingRepository)
+ abstract getUsersList(): ?array
+ abstract getUserList(user: User): User
+ abstract getTraining(): ?TrainingRepository
+ abstract getTrainingsList(): ?array
+ abstract getTrainingList(training: Training): ?Training
+ abstract CheckAdd(user: User): bool
+ abstract CheckAddTraining(training: Training): bool
+ abstract addUser(user: User): bool
+ abstract removeUser(user: User): bool
+ abstract addTraining(training: Training): bool
+ abstract removeTraining(training: Training): bool
}
class User {
- int id
- String username
- string nom
- string prenom
- string email
- string motDePasse
- string sexe
- float taille
- float poids
- DateTime dateNaissance
+ __construct(id: int, username: String, nom: string, prenom: string, email: string, motDePasse: string, sexe: string, taille: float, poids: float, dateNaissance: DateTime, role: Role)
+ getId(): int
+ setId(id: int): void
+ getUsername(): String
+ setUsername(username: int): 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(): DateTime
+ setDateNaissance(dateNaissance: DateTime): void
+ getRole(): Role
+ setRole(role: Role): void
+ isValidPassword(password: string): bool
+ __toString(): String
}
class Statistique {
- idStat: int
- distanceTotale: float
- poids: float
- tempsTotal: time
- FCmoyenne: int
- FCmin: int
- FCmax: int
- cloriesBrulees: int
+ getIdStat(): int
+ getDistanceTotale(): float
+ getPoids(): float
+ getTempsTotal(): time
+ getFCmoyenne(): int
+ getFCmin(): int
+ getFCmax(): int
+ getCloriesBrulees(): int
+ __toString(): String
}
CoachAthlete --|> Coach
Coach --|> Role
Athlete --|> Role
User -> Role : role
Role -> User : usersList
Athlete -> Statistique : statsList
@enduml
````

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

@ -1,49 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Diagramme de Séquence : Gestion des Demandes d'Amis
Bienvenue dans le processus dynamique de gestion des demandes d'amis au sein de notre application ! Ce diagramme de séquence met en évidence les étapes clés impliquées dans la gestion des demandes d'amis entre utilisateurs.
**Acteurs Principaux :**
- **Utilisateur (u) :** L'individu interagissant avec l'application, recevant et répondant aux demandes d'amis.
**Flux d'Interaction :**
1. **Réception d'une Demande d'Ami :** Lorsqu'un utilisateur reçoit une demande d'ami, le modèle (Model) notifie le contrôleur (Controller) de la nouvelle demande, spécifiant l'identifiant de l'utilisateur émetteur.
2. **Affichage de la Demande d'Ami :** Le contrôleur transmet l'information à la vue (View), qui affiche la demande d'ami à l'utilisateur.
3. **Affichage de la Page des Demandes d'Amis :** L'utilisateur visualise la page des demandes d'amis dans l'interface utilisateur.
4. **Réponse à la Demande d'Ami :** L'utilisateur prend une décision quant à la demande d'ami, en répondant par un choix binaire (accepter ou refuser).
5. **Enregistrement de la Réponse :** La vue (View) transmet la réponse de l'utilisateur au contrôleur, qui enregistre cette réponse.
6. **Envoi de la Réponse :** Le contrôleur communique avec le modèle pour envoyer la réponse, indiquant si la demande a été acceptée (true) ou refusée (false).
À travers ce diagramme de séquence, découvrez comment notre application gère efficacement le processus de gestion des demandes d'amis, offrant aux utilisateurs une expérience transparente et réactive lors de l'établissement de connexions sociales au sein de la plateforme.
````plantuml
@startuml
actor User as u
boundary View as v
control Controller as c
entity Model as m
m-->c: pendingRequests: Request[]
c-->v: DisplayPendingRequests(pendingRequests)
v-->u: Show Friend Requests
u->v: RespondToRequest(requestId, response)
v-->c: RecordResponse(requestId, response)
c->m: UpdateRequestStatus(requestId, response)
m-->c: updateStatus: success/failure
c-->v: NotifyUpdateResult(updateStatus)
v-->u: Show Response Result
@enduml
``````

@ -1,30 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Diagramme de Séquence : Recherche d'Amis
Bienvenue dans le processus dynamique de recherche d'amis au sein de notre application ! Ce diagramme de séquence met en lumière les étapes clés impliquées lorsque les utilisateurs recherchent des amis en utilisant un pseudo spécifique.
**Acteurs Principaux :**
- **Utilisateur (u) :** L'individu interagissant avec l'application, initié à la recherche d'amis.
**Flux d'Interaction :**
1. **Accès à la Fonctionnalité de Recherche :** L'utilisateur déclenche la fonctionnalité de recherche d'amis depuis son interface utilisateur.
2. **Saisie du Pseudo :** L'utilisateur entre le pseudo de l'ami qu'il souhaite rechercher.
3. **Requête de Recherche :** La vue (View) transmet la demande de recherche au contrôleur (Controller), qui déclenche une requête GET au serveur pour récupérer la liste des amis correspondant au pseudo saisi.
4. **Traitement de la Requête :** Le modèle (Model) récupère la liste d'amis correspondante en utilisant l'identifiant de l'utilisateur et notifie le contrôleur du résultat.
5. **Notification des Utilisateurs :** Le modèle informe également les utilisateurs concernés (émetteur et destinataire) de l'action de recherche effectuée.
6. **Rendu de la Vue :** Le contrôleur reçoit la liste d'amis du modèle et rend cette liste à la vue.
7. **Affichage des Résultats :** La vue affiche les résultats de la recherche à l'utilisateur, montrant les amis qui correspondent au pseudo saisi.
À travers ce diagramme de séquence, découvrez comment notre application facilite le processus de recherche d'amis, fournissant aux utilisateurs une interface conviviale et réactive pour élargir leur réseau social au sein de la plateforme.
<img src="AjouterAmis.png" alt="Diagramme de Séquence : Recherche d'Amis" width="1000"/>

@ -1,63 +0,0 @@
[retour au README.md](../../../README.md)
[Retour au diagramme de classes](../README_DIAGRAMMES.md)
# Introduction au Diagramme de Séquence : Gestion des Amis
Bienvenue dans le processus dynamique de gestion des amis au sein de notre application ! Ce diagramme de séquence met en lumière les interactions entre l'utilisateur et l'application, ainsi que le flux d'informations entre les différentes composantes du système.
**Acteurs Principaux :**
- **Utilisateur (u) :** L'individu interagissant avec l'application, souhaitant consulter et gérer sa liste d'amis.
**Flux d'Interaction :**
1. **Demande de la Page d'Amis :** L'utilisateur déclenche la demande de la page d'amis, amorçant le processus d'affichage de sa liste d'amis.
2. **Récupération des Amis :** Le contrôleur (Controller) reçoit la demande et interagit avec le modèle (Model) pour récupérer la liste d'amis associée à l'identifiant de l'utilisateur.
- *Cas de Récupération Réussi :* Si la récupération est réussie, le modèle transmet la liste d'amis au contrôleur.
- *Cas d'Échec de Récupération :* En cas d'échec, une notification d'erreur est renvoyée.
3. **Affichage de la Liste d'Amis :** Le contrôleur rend la vue (View) en utilisant la liste d'amis récupérée, qui est ensuite affichée à l'utilisateur.
4. **Suppression d'un Ami :** L'utilisateur décide de supprimer un ami spécifique en cliquant sur l'option correspondante.
5. **Traitement de la Suppression :** Le contrôleur, en réponse à la demande de suppression, envoie une requête au modèle pour effectuer la suppression de l'ami identifié par son identifiant utilisateur (idUser).
- *Cas de Suppression Réussie :* Si la suppression est réussie, le modèle renvoie la liste d'amis mise à jour.
- *Cas d'Échec de Suppression :* En cas d'échec, une notification d'erreur est renvoyée.
6. **Affichage de la Liste d'Amis Mise à Jour :** La vue est mise à jour avec la nouvelle liste d'amis, qui est ensuite affichée à l'utilisateur.
À travers ce diagramme de séquence, découvrez comment notre application gère de manière fluide et réactive les interactions de l'utilisateur avec sa liste d'amis, garantissant une expérience utilisateur cohérente et sans heurts.
```plantuml
actor User as u
boundary View as v
control Controller as c
entity Model as m
u->v: Request Friends Page
v->c: Get /Friends
c->m: getFriends(userId)
alt successful retrieval
m-->c: friendsList: User[]
else retrieval failed
m-->c: error
end
c-->v: renderView(friendsList)
v-->u: Display Friends
u->v: clickDeleteFriend(idUser)
v->c: Post: deleteFriend(idUser)
c->m: deleteFriend(idUser)
alt successful deletion
m-->c: updatedFriendsList: User[]
else deletion failed
m-->c: error
end
c-->v: renderView(updatedFriendsList)
v-->u: Display Updated Friends
```

@ -1,21 +0,0 @@
[retour au README.md](../../README.md)
# Diagrammes nécéssaires à notre projet
## Diagrammes de classes
- [issue016 - Statistiques coach ](DiagrammeDeClasses/README_issue016.md)
- [couche d'accès aux données](DiagrammeDeClasses/README_accesDonnees.md)
- [Diagramme général](DiagrammeDeClasses/README_DIAGRAMME.md)
## Diagrammes de séquence
- [Envoi de demande d'ami](DiagrammeDeSequence/README_demandeAmi.md)
- [Accepter une demande d'ami](DiagrammeDeSequence/README_accepterAmi.md)
- [Supprimer un ami](DiagrammeDeSequence/README_suppressionAmi.md)
- [issue021 - Authentification ](DiagrammeDeSequence/README_issue021.md)
## Diagrammes de cas d'utilisation
- [Cas d'utilisation pour la gestion du compte et des amitiés](CasUtilisations/README_gestionCompteAmitie.md)
- [Cas d'utilisation pour la gestion des activités et données](CasUtilisations/README_gestionActivites.md)
## Base de données
- [MCD - MLD](BDD/README_BDD.md)

@ -1,6 +1,4 @@
using Dto; using Dto;
using Dto.Tiny;
using Entities;
using Model; using Model;
using Shared; using Shared;
@ -9,9 +7,8 @@ namespace APIMappers;
public static class ActivityMapper public static class ActivityMapper
{ {
private static GenericMapper<Activity, ActivityDto> _mapper = new(); private static GenericMapper<Activity, ActivityDto> _mapper = new();
private static GenericMapper<ActivityTinyDto, ActivityEntity> _mapperTiny = new ();
public static Activity ToModel(this ActivityDto activityDto, User user) public static Activity ToModel(this ActivityDto activityDto)
{ {
Func<ActivityDto, Activity> create = activity => new Activity Func<ActivityDto, Activity> create = activity => new Activity
{ {
@ -33,15 +30,12 @@ public static class ActivityMapper
Action<ActivityDto, Activity> link = (activity, model) => Action<ActivityDto, Activity> link = (activity, model) =>
{ {
if (activity.DataSource != null) model.HeartRates.AddRange(activity.HeartRates.ToModels(activityDto.Id).ToArray());
model.DataSource = user.DataSources.FirstOrDefault(ds => ds.Id == activity.DataSource.Id); if (activity.DataSource != null) model.DataSource = activity.DataSource.ToModel();
model.Athlete = user; model.Athlete = activity.Athlete.ToModel();
}; };
var act = activityDto.ToT(_mapper, create, link); return activityDto.ToT(_mapper, create, link);
act.HeartRates.AddRange(activityDto.HeartRates.ToModels(act).ToList());
return act;
} }
public static ActivityDto ToDto(this Activity model) public static ActivityDto ToDto(this Activity model)
@ -61,8 +55,7 @@ public static class ActivityMapper
Maximum = activity.Maximum, Maximum = activity.Maximum,
Minimum = activity.Minimum, Minimum = activity.Minimum,
AverageTemperature = activity.AverageTemperature, AverageTemperature = activity.AverageTemperature,
HasAutoPause = activity.HasAutoPause, HasAutoPause = activity.HasAutoPause
AthleteId = activity.Athlete.Id
}; };
Action<Activity, ActivityDto> link = (activity, dto) => Action<Activity, ActivityDto> link = (activity, dto) =>
@ -75,30 +68,8 @@ public static class ActivityMapper
return model.ToU(_mapper, create, link); return model.ToU(_mapper, create, link);
} }
public static ActivityEntity ToEntity(this ActivityTinyDto tinyDto) public static IEnumerable<Activity> ToModels(this IEnumerable<ActivityDto> dtos)
{ => dtos.Select(dto => dto.ToModel());
Func<ActivityTinyDto, ActivityEntity> create = dto => new ActivityEntity
{
Type = dto.Type,
Date = DateOnly.FromDateTime(dto.Date),
StartTime = TimeOnly.FromDateTime(dto.StartTime),
EndTime = TimeOnly.FromDateTime(dto.EndTime),
EffortFelt = dto.EffortFelt,
Variability = dto.Variability,
Variance = dto.Variance,
StandardDeviation = dto.StandardDeviation,
Average = dto.Average,
Maximum = dto.Maximum,
Minimum = dto.Minimum,
AverageTemperature = dto.AverageTemperature,
HasAutoPause = dto.HasAutoPause
};
return tinyDto.ToU(_mapperTiny, create);
}
public static IEnumerable<Activity> ToModels(this IEnumerable<ActivityDto> dtos, User user)
=> dtos.Select(dto => dto.ToModel(user));
public static IEnumerable<ActivityDto> ToDtos(this IEnumerable<Activity> models) public static IEnumerable<ActivityDto> ToDtos(this IEnumerable<Activity> models)
=> models.Select(model => model.ToDto()); => models.Select(model => model.ToDto());

@ -9,11 +9,10 @@ public static class DataSourceMapper
private static GenericMapper<DataSource, DataSourceDto> _mapper = new (); private static GenericMapper<DataSource, DataSourceDto> _mapper = new ();
public static DataSource ToModel(this DataSourceDto dto, User user) public static DataSource ToModel(this DataSourceDto dto)
{ {
Func<DataSourceDto, DataSource> create = dataSourceDto => Func<DataSourceDto, DataSource> create = dataSourceDto =>
new DataSource(dataSourceDto.Id, dataSourceDto.Type, dataSourceDto.Model, dataSourceDto.Precision, new DataSource( dataSourceDto.Id, dataSourceDto.Type, dataSourceDto.Model, dataSourceDto.Precision, dataSourceDto.Athletes.ToModels().ToList(), dataSourceDto.Activities.ToModels().ToList());
new List<User> { user }, user.Activities.Where(a => a.DataSource.Id == dataSourceDto.Id).ToList());
/* /*
Action<DataSourceDto, DataSource> link = (dataSourceDto, model) => Action<DataSourceDto, DataSource> link = (dataSourceDto, model) =>
{ {
@ -42,8 +41,8 @@ public static class DataSourceMapper
return model.ToU(_mapper, create, link); return model.ToU(_mapper, create, link);
} }
public static IEnumerable<DataSource> ToModels(this IEnumerable<DataSourceDto> dtos, User user) public static IEnumerable<DataSource> ToModels(this IEnumerable<DataSourceDto> dtos)
=> dtos.Select(d => d.ToModel(user)); => dtos.Select(d => d.ToModel());
public static IEnumerable<DataSourceDto> ToDtos(this IEnumerable<DataSource> models) public static IEnumerable<DataSourceDto> ToDtos(this IEnumerable<DataSource> models)
=> models.Select(m => m.ToDto()); => models.Select(m => m.ToDto());

@ -8,16 +8,17 @@ public static class HeartRateMapper
{ {
private static GenericMapper<HeartRate, HeartRateDto> _mapper = new(); private static GenericMapper<HeartRate, HeartRateDto> _mapper = new();
public static HeartRate ToModel(this HeartRateDto dto,Activity activityDto) public static HeartRate ToModel(this HeartRateDto dto,int activityDtoId)
{ {
Func<HeartRateDto, HeartRate> create = heartRateDto => Func<HeartRateDto, HeartRate> create = heartRateDto =>
new HeartRate(heartRateDto.HeartRate, TimeOnly.FromDateTime(heartRateDto.Timestamp), activityDto, heartRateDto.Latitude, heartRateDto.Longitude, heartRateDto.Altitude, heartRateDto.Cadence, heartRateDto.Distance, heartRateDto.Speed, heartRateDto.Power, heartRateDto.Temperature); new HeartRate(heartRateDto.HeartRate, TimeOnly.FromDateTime(heartRateDto.Timestamp), activityDtoId, heartRateDto.Latitude, heartRateDto.Longitude, heartRateDto.Altitude, heartRateDto.Cadence, heartRateDto.Distance, heartRateDto.Speed, heartRateDto.Power, heartRateDto.Temperature);
return dto.ToT(_mapper, create); return dto.ToT(_mapper, create);
} }
public static HeartRateDto ToDto(this HeartRate model)//Activity activity public static HeartRateDto ToDto(this HeartRate model)//Activity activity
{ {
// [TODO] [Dave] fix this should be activity but it boucle indefinitly
var activity = new DateTime(); var activity = new DateTime();
Func<HeartRate, HeartRateDto> create = heartRate => Func<HeartRate, HeartRateDto> create = heartRate =>
new HeartRateDto new HeartRateDto
@ -36,8 +37,8 @@ public static class HeartRateMapper
return model.ToU(_mapper, create); return model.ToU(_mapper, create);
} }
public static IEnumerable<HeartRate> ToModels(this IEnumerable<HeartRateDto> dtos, Activity activityDto) public static IEnumerable<HeartRate> ToModels(this IEnumerable<HeartRateDto> dtos, int activityDtoId)
=> dtos.Select(d => d.ToModel(activityDto)); => dtos.Select(d => d.ToModel(activityDtoId));
public static IEnumerable<HeartRateDto> ToDtos(this IEnumerable<HeartRate> models) public static IEnumerable<HeartRateDto> ToDtos(this IEnumerable<HeartRate> models)
=> models.Select(m => m.ToDto()); => models.Select(m => m.ToDto());

@ -7,8 +7,6 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using Entities; using Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DbContextLib namespace DbContextLib
@ -16,7 +14,7 @@ namespace DbContextLib
/// <summary> /// <summary>
/// Represents the database context for the FitnessApp. /// Represents the database context for the FitnessApp.
/// </summary> /// </summary>
public class HeartTrackContext : IdentityDbContext<AthleteEntity,IdentityRole<int>,int> public class HeartTrackContext : DbContext
{ {
/// <summary> /// <summary>
/// Gets or sets the set of athletes. /// Gets or sets the set of athletes.
@ -134,10 +132,10 @@ namespace DbContextLib
//primary key of AthleteEntity //primary key of AthleteEntity
modelBuilder.Entity<AthleteEntity>() modelBuilder.Entity<AthleteEntity>()
.HasKey(at => at.Id); .HasKey(at => at.IdAthlete);
//generation mode (at insertion) //generation mode (at insertion)
modelBuilder.Entity<AthleteEntity>() modelBuilder.Entity<AthleteEntity>()
.Property(at => at.Id) .Property(at => at.IdAthlete)
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
// add image column type // add image column type
// modelBuilder.Entity<AthleteEntity>() // modelBuilder.Entity<AthleteEntity>()
@ -170,16 +168,16 @@ namespace DbContextLib
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
modelBuilder.Entity<FriendshipEntity>() modelBuilder.Entity<FriendshipEntity>()
.HasKey(f => new { f.FollowerId, f.FollowingId }); .HasKey(f => new { f.FollowingId, f.FollowerId });
modelBuilder.Entity<FriendshipEntity>() modelBuilder.Entity<FriendshipEntity>()
.HasOne(fing => fing.Following) .HasOne(fing => fing.Following)
.WithMany(fings => fings.Followers) .WithMany(fings => fings.Followings)
.HasForeignKey(fing => fing.FollowingId); .HasForeignKey(fing => fing.FollowingId);
modelBuilder.Entity<FriendshipEntity>() modelBuilder.Entity<FriendshipEntity>()
.HasOne(fer => fer.Follower) .HasOne(fer => fer.Follower)
.WithMany(fers => fers.Followings) .WithMany(fers => fers.Followers)
.HasForeignKey(fing => fing.FollowerId); .HasForeignKey(fing => fing.FollowerId);
// ! // !
@ -208,22 +206,19 @@ namespace DbContextLib
.HasMany(at => at.Statistics) .HasMany(at => at.Statistics)
.WithOne(s => s.Athlete) .WithOne(s => s.Athlete)
.HasForeignKey(s => s.AthleteId) .HasForeignKey(s => s.AthleteId)
.IsRequired(false) .IsRequired(false);
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<AthleteEntity>() modelBuilder.Entity<AthleteEntity>()
.HasMany(at => at.Activities) .HasMany(at => at.Activities)
.WithOne(a => a.Athlete) .WithOne(a => a.Athlete)
.HasForeignKey(a => a.AthleteId) .HasForeignKey(a => a.AthleteId)
.IsRequired(false) .IsRequired(false);
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ActivityEntity>() modelBuilder.Entity<ActivityEntity>()
.HasMany(a => a.HeartRates) .HasMany(a => a.HeartRates)
.WithOne(h => h.Activity) .WithOne(h => h.Activity)
.HasForeignKey(h => h.ActivityId) .HasForeignKey(h => h.ActivityId)
.IsRequired() .IsRequired();
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<DataSourceEntity>() modelBuilder.Entity<DataSourceEntity>()
.HasMany(d => d.Activities) .HasMany(d => d.Activities)
@ -235,24 +230,16 @@ namespace DbContextLib
.HasMany(ds => ds.Athletes) .HasMany(ds => ds.Athletes)
.WithOne(at => at.DataSource) .WithOne(at => at.DataSource)
.HasForeignKey(at => at.DataSourceId) .HasForeignKey(at => at.DataSourceId)
.IsRequired(false); .IsRequired();
List<IdentityRole<int>> roles =
[
new()
{
Id = 1,
Name = "Athlete",
NormalizedName = "ATHLETE"
},
new() // modelBuilder.Entity<AthleteEntity>()
{ // .HasMany(fer => fer.Followers)
Id = 2, // .WithMany(fing => fing.Followings)
Name = "Coach", // .UsingEntity<FriendshipEntity>(
NormalizedName = "COACH" // l => l.HasOne<AthleteEntity>().WithMany().HasForeignKey(fer => fer.FollowerId),
} // r => r.HasOne<AthleteEntity>().WithMany().HasForeignKey(fing => fing.FollowingId),
]; // j => j.Property(f => f.StartDate).HasDefaultValueSql("CURRENT_TIMESTAMP")
modelBuilder.Entity<IdentityRole<int>>().HasData(roles); // );
} }
} }
} }

@ -0,0 +1,25 @@
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");
}
}*/
}

@ -1,5 +1,3 @@
using Newtonsoft.Json;
namespace Dto; namespace Dto;
public class ActivityDto public class ActivityDto
@ -18,19 +16,10 @@ public class ActivityDto
public int Minimum { get; set; } public int Minimum { get; set; }
public float AverageTemperature { get; set; } public float AverageTemperature { get; set; }
public bool HasAutoPause { get; set; } public bool HasAutoPause { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public DataSourceDto? DataSource { get; set; } public DataSourceDto? DataSource { get; set; }
[JsonProperty("DataSourceId")]
public int? DataSourceId { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
public UserDto? Athlete { get; set; } public UserDto? Athlete { get; set; }
[JsonProperty("AthleteId")]
public int AthleteId { get; set; }
// public int? TrainingId { get; set; } // public int? TrainingId { get; set; }
public IEnumerable<HeartRateDto> HeartRates { get; set; } public IEnumerable<HeartRateDto> HeartRates { get; set; }
} }

@ -12,12 +12,14 @@ public class UserDto
[MaxLength(100)] [MaxLength(100)]
public required string FirstName { get; set; } public required string FirstName { get; set; }
public required string Email { get; set; } public required string Email { get; set; }
public required char Sexe { get; set; } public required string Sexe { get; set; }
public float Lenght { get; set; } public float Lenght { get; set; }
public float Weight { get; set; } public float Weight { get; set; }
public string? Password { get; set; } public string? Password { get; set; }
public DateTime DateOfBirth { get; set; } public DateTime DateOfBirth { get; set; }
public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png"; public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
public LargeImageDto? Image { get; set; }
public LargeImageDto Image { get; set; }
public bool IsCoach { get; set; } public bool IsCoach { get; set; }
} }

@ -1,13 +0,0 @@
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; }
}

@ -1,23 +0,0 @@
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; }
}

@ -1,33 +0,0 @@
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; }
}

@ -12,9 +12,11 @@ public class DataSourceDto
public float Precision { get; set; } public float Precision { get; set; }
// [TODO] [Dave] Add a property to store the athletes and the activities so maybe adapt to have a tiny DTO
[JsonIgnore] [JsonIgnore]
public IEnumerable<UserDto>? Athletes { get; set; } public IEnumerable<UserDto>? Athletes { get; set; }
// [TODO] [Dave] Add a property to store the athletes and the activities so maybe adapt to have a tiny DTO
[JsonIgnore] [JsonIgnore]
public IEnumerable<ActivityDto>? Activities { get; set; } public IEnumerable<ActivityDto>? Activities { get; set; }
} }

@ -6,8 +6,4 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project> </Project>

@ -1,12 +0,0 @@
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; }
}

@ -1,25 +0,0 @@
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; }
}

@ -1,18 +0,0 @@
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; }
}

@ -1,28 +0,0 @@
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; }
}

@ -1,19 +0,0 @@
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; }
}

@ -1,12 +0,0 @@
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; }
}

@ -1,7 +0,0 @@
namespace Dto.Tiny;
public class FriendshipDto
{
public int FollowedId { get; set; }
public int FollowerId { get; set; }
}

@ -1,16 +0,0 @@
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; }
}

@ -1,22 +0,0 @@
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; }
}

@ -64,7 +64,7 @@ public static class ActivityMapper
Action<Activity, ActivityEntity> link = (activity, entity) => Action<Activity, ActivityEntity> link = (activity, entity) =>
{ {
entity.HeartRates = activity.HeartRates.ToEntities().ToList(); entity.HeartRates = activity.HeartRates.ToEntities().ToList();
entity.DataSource = activity.DataSource != null ? activity.DataSource.ToEntity() : null; entity.DataSource = activity.DataSource.ToEntity();
entity.Athlete = activity.Athlete.ToEntity(); entity.Athlete = activity.Athlete.ToEntity();
}; };
return model.ToU(_mapper, create, link); return model.ToU(_mapper, create, link);

@ -9,23 +9,18 @@ public static class UserMappeur
{ {
private static GenericMapper<User, AthleteEntity> _mapper = new (); private static GenericMapper<User, AthleteEntity> _mapper = new ();
public static void Reset()
{
_mapper.Reset();
}
public static User ToModel(this AthleteEntity entity) public static User ToModel(this AthleteEntity entity)
{ {
Func<AthleteEntity, User> create = athleteEntity => new User Func<AthleteEntity, User> create = athleteEntity => new User
{ {
Id = athleteEntity.Id, Id = athleteEntity.IdAthlete,
FirstName = athleteEntity.FirstName, FirstName = athleteEntity.FirstName,
LastName = athleteEntity.LastName, LastName = athleteEntity.LastName,
Email = athleteEntity.Email, Email = athleteEntity.Email,
MotDePasse = athleteEntity.PasswordHash, MotDePasse = athleteEntity.Password,
DateOfBirth = athleteEntity.DateOfBirth.ToDateTime(TimeOnly.MinValue), DateOfBirth = athleteEntity.DateOfBirth.ToDateTime(TimeOnly.MinValue),
Sexe = athleteEntity.Sexe, Sexe = athleteEntity.Sexe,
Username = athleteEntity.UserName, Username = athleteEntity.Username,
Weight = athleteEntity.Weight, Weight = athleteEntity.Weight,
Lenght = (float)athleteEntity.Length, Lenght = (float)athleteEntity.Length,
ProfilePicture = athleteEntity.ProfilPicture, ProfilePicture = athleteEntity.ProfilPicture,
@ -35,8 +30,8 @@ public static class UserMappeur
Action<AthleteEntity, User> link = (athleteEntity, model) => Action<AthleteEntity, User> link = (athleteEntity, model) =>
{ {
model.Role = athleteEntity.IsCoach ? new Coach() : new Athlete(); model.Role = athleteEntity.IsCoach ? new Coach() : new Athlete();
if (athleteEntity.DataSource != null) model.DataSources.Add(athleteEntity.DataSource.ToModel()); model.DataSources.Add(athleteEntity.DataSource.ToModel());
if (athleteEntity.Activities != null) model.Activities.AddRange(athleteEntity.Activities.ToModels()); model.Activities.AddRange(athleteEntity.Activities.ToModels());
//model.Image = athleteEntity.Image.ToModel(); //model.Image = athleteEntity.Image.ToModel();
}; };
@ -47,13 +42,13 @@ public static class UserMappeur
{ {
Func<User, AthleteEntity> create = user => new AthleteEntity Func<User, AthleteEntity> create = user => new AthleteEntity
{ {
Id = user.Id, IdAthlete = user.Id,
UserName = user.Username, Username = user.Username,
Sexe = user.Sexe, Sexe = user.Sexe,
FirstName = user.FirstName, FirstName = user.FirstName,
LastName = user.LastName, LastName = user.LastName,
Email = user.Email, Email = user.Email,
PasswordHash = user.MotDePasse, Password = user.MotDePasse,
DateOfBirth = DateOnly.FromDateTime(user.DateOfBirth), DateOfBirth = DateOnly.FromDateTime(user.DateOfBirth),
IsCoach = user.Role is Coach, IsCoach = user.Role is Coach,
Weight = user.Weight, Weight = user.Weight,
@ -67,6 +62,17 @@ public static class UserMappeur
entity.Activities = user.Activities.ToEntities().ToList(); entity.Activities = user.Activities.ToEntities().ToList();
entity.IsCoach = user.Role is Coach; entity.IsCoach = user.Role is Coach;
entity.Image = user.Image.ToEntity(); entity.Image = user.Image.ToEntity();
/*if (user.Role is Coach)
entity.TrainingsCoach = user.Traning.ToEntities().ToList();
else
entity.TrainingsAthlete = user.Traning.ToEntities().ToList();
*/
// entity.NotificationsReceived = user.Notifications.ToEntities().ToList();
// entity.DataSource = user.DataSources.ToEntities().ToList();
// [TODO] [DAVE] : Add the link to the friendship
}; };
return model.ToU(_mapper, create, link); return model.ToU(_mapper, create, link);

@ -3,7 +3,7 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@ -105,6 +105,6 @@ namespace Entities
public int AthleteId { get; set; } public int AthleteId { get; set; }
public AthleteEntity Athlete { get; set; } public AthleteEntity Athlete { get; set; } = null!;
} }
} }

@ -3,63 +3,61 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity;
namespace Entities namespace Entities
{ {
//dentityRole<int>,int
/// <summary> /// <summary>
/// Represents an athlete entity in the database. /// Represents an athlete entity in the database.
/// </summary> /// </summary>
[Table("Athlete")] [Table("Athlete")]
public class AthleteEntity : IdentityUser<int> public class AthleteEntity
{ {
/// <summary> /// <summary>
/// Gets or sets the unique identifier of the athlete. /// Gets or sets the unique identifier of the athlete.
/// </summary> /// </summary>
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; } public int IdAthlete { get; set; }
/// <summary> /// <summary>
/// Gets or sets the username of the athlete. /// Gets or sets the username of the athlete.
/// </summary> /// </summary>
[MaxLength(100)] [MaxLength(100)]
[Required(ErrorMessage = "Athlete Username is ")] [Required(ErrorMessage = "Athlete Username is ")]
public override string UserName { get; set; } public required string Username { get; set; }
/// <summary> /// <summary>
/// Gets or sets the last name of the athlete. /// Gets or sets the last name of the athlete.
/// </summary> /// </summary>
[MaxLength(100)] [MaxLength(100)]
[Required(ErrorMessage = "Athlete Last Name is ")] [Required(ErrorMessage = "Athlete Last Name is ")]
public string LastName { get; set; } public required string LastName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the first name of the athlete. /// Gets or sets the first name of the athlete.
/// </summary> /// </summary>
[MaxLength(150)] [MaxLength(150)]
[Required(ErrorMessage = "Athlete First Name is ")] [Required(ErrorMessage = "Athlete First Name is ")]
public string FirstName { get; set; } public required string FirstName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the email of the athlete. /// Gets or sets the email of the athlete.
/// </summary> /// </summary>
[MaxLength(100)] [MaxLength(100)]
[Required(ErrorMessage = "Athlete Email is ")] [Required(ErrorMessage = "Athlete Email is ")]
public override string Email { get; set; } public required string Email { get; set; }
/// <summary> /// <summary>
/// Gets or sets the gender of the athlete. /// Gets or sets the gender of the athlete.
/// </summary> /// </summary>
[MaxLength(1)] [MaxLength(1)]
[Required(ErrorMessage = "Athlete Sexe is ")] [Required(ErrorMessage = "Athlete Sexe is ")]
public char Sexe { get; set; } public required string Sexe { get; set; }
/// <summary> /// <summary>
/// Gets or sets the height of the athlete. /// Gets or sets the height of the athlete.
@ -70,12 +68,12 @@ namespace Entities
/// Gets or sets the weight of the athlete. /// Gets or sets the weight of the athlete.
/// </summary> /// </summary>
public float Weight { get; set; } public float Weight { get; set; }
/*
/// <summary> /// <summary>
/// Gets or sets the password of the athlete. /// Gets or sets the password of the athlete.
/// </summary> /// </summary>
[Required(ErrorMessage = "Athlete PasswordHash is ")] [Required(ErrorMessage = "Athlete Password is ")]
public string PasswordHash { get; set; }*/ public required string Password { get; set; }
/// <summary> /// <summary>
/// Gets or sets the date of birth of the athlete. /// Gets or sets the date of birth of the athlete.
@ -89,6 +87,7 @@ namespace Entities
/// </summary> /// </summary>
public bool IsCoach { get; set; } public bool IsCoach { get; set; }
// [TODO] [DAVE] Pourquoi c'est un byte[] ? et pas un string ? it's me so should change it to string
public string? ProfilPicture { get; set; } public string? ProfilPicture { get; set; }
public LargeImageEntity? Image { get; set; } public LargeImageEntity? Image { get; set; }
@ -107,6 +106,7 @@ namespace Entities
public ICollection<NotificationEntity> NotificationsSent { get; set; } = new List<NotificationEntity>(); public ICollection<NotificationEntity> NotificationsSent { get; set; } = new List<NotificationEntity>();
public int? DataSourceId { get; set; } public int? DataSourceId { get; set; }
// [TODO] [DAVE] Pourquoi c'est un one to one ? et pas un one to many ?
public DataSourceEntity? DataSource { get; set; } public DataSourceEntity? DataSource { get; set; }
public ICollection<FriendshipEntity> Followers { get; set; } = []; public ICollection<FriendshipEntity> Followers { get; set; } = [];

@ -3,7 +3,7 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;

@ -5,10 +5,4 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="8.0.3" />
</ItemGroup>
</Project> </Project>

@ -3,7 +3,7 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;

@ -3,7 +3,7 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;

@ -3,7 +3,7 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;

@ -3,7 +3,7 @@
// PROJECT: Entities // PROJECT: Entities
// SOLUTION: HeartTrack // SOLUTION: HeartTrack
// DATE CREATED: 22/02/2024 // DATE CREATED: 22/02/2024
// AUTHOR: HeartTeam // AUTHOR: Antoine PEREDERII
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;

@ -1,101 +0,0 @@
using Dto;
using Dto.Tiny;
using Entities;
using Shared;
namespace Entities2Dto;
public static class ActivityMapper
{
private static GenericMapper<ActivityTinyDto, ActivityEntity> _mapper = new ();
private static GenericMapper<ResponseActivityDto, ActivityEntity> _mapperFull = new ();
public static void Reset()
{
_mapper.Reset();
_mapperFull.Reset();
}
public static ActivityTinyDto ToDto(this ActivityEntity entity)
{
Func<ActivityEntity, ActivityTinyDto> create = activityEntity => new ActivityTinyDto
{
Id = activityEntity.IdActivity,
Type = activityEntity.Type,
Date = activityEntity.Date.ToDateTime(TimeOnly.MinValue),
StartTime = activityEntity.Date.ToDateTime(activityEntity.StartTime),
EndTime = activityEntity.Date.ToDateTime(activityEntity.EndTime),
EffortFelt = activityEntity.EffortFelt,
Variability = activityEntity.Variability,
Variance = activityEntity.Variance,
StandardDeviation = activityEntity.StandardDeviation,
Average = activityEntity.Average,
Maximum = activityEntity.Maximum,
Minimum = activityEntity.Minimum,
AverageTemperature = activityEntity.AverageTemperature,
HasAutoPause = activityEntity.HasAutoPause
};
return entity.ToT(_mapper, create, null, false);
}
public static ActivityEntity ToEntity(this ActivityTinyDto dto)
{
Func<ActivityTinyDto, ActivityEntity> create = activity => new ActivityEntity
{
Type = activity.Type,
Date = DateOnly.FromDateTime(activity.Date),
StartTime = TimeOnly.FromDateTime(activity.StartTime),
EndTime = TimeOnly.FromDateTime(activity.EndTime),
EffortFelt = activity.EffortFelt,
Variability = activity.Variability,
Variance = activity.Variance,
StandardDeviation = activity.StandardDeviation,
Average = activity.Average,
Maximum = activity.Maximum,
Minimum = activity.Minimum,
AverageTemperature = activity.AverageTemperature,
HasAutoPause = activity.HasAutoPause
};
return dto.ToU(_mapper, create);
}
public static ResponseActivityDto ToResponseDto(this ActivityEntity entity)
{
Func<ActivityEntity, ResponseActivityDto> create = activityEntity => new ResponseActivityDto
{
Id = activityEntity.IdActivity,
Type = activityEntity.Type,
Date = activityEntity.Date.ToDateTime(TimeOnly.MinValue),
StartTime = activityEntity.Date.ToDateTime(activityEntity.StartTime),
EndTime = activityEntity.Date.ToDateTime(activityEntity.EndTime),
EffortFelt = activityEntity.EffortFelt,
Variability = activityEntity.Variability,
Variance = activityEntity.Variance,
StandardDeviation = activityEntity.StandardDeviation,
Average = activityEntity.Average,
Maximum = activityEntity.Maximum,
Minimum = activityEntity.Minimum,
AverageTemperature = activityEntity.AverageTemperature,
HasAutoPause = activityEntity.HasAutoPause
};
Action<ActivityEntity, ResponseActivityDto> linker = (activityEntity, activity) =>
{
if (activityEntity.HeartRates != null) activity.HeartRates = activityEntity.HeartRates.ToTinyDtos().ToArray();
activity.DataSource = activityEntity.DataSource != null ? activityEntity.DataSource.ToTinyDto() : null;
activity.Athlete = activityEntity.Athlete.ToTinyDto();
};
return entity.ToT(_mapperFull, create, linker, false);
}
public static IEnumerable<ActivityTinyDto> ToTinyDtos(this IEnumerable<ActivityEntity> entities)
=> entities.Select(a => a.ToDto());
public static IEnumerable<ActivityEntity> ToEntities(this IEnumerable<ActivityTinyDto> dtos)
=> dtos.Select(a => a.ToEntity());
}

@ -1,70 +0,0 @@
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());
}

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Entities\Entities.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Identity.Stores">
<HintPath>..\..\..\.nuget\packages\microsoft.extensions.identity.stores\8.0.2\lib\net8.0\Microsoft.Extensions.Identity.Stores.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

@ -1,32 +0,0 @@
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());
}

@ -1,63 +0,0 @@
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());
}

@ -1,32 +0,0 @@
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);
}
}

@ -1,102 +0,0 @@
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());
}

@ -3,55 +3,52 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59 VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbContextLib", "DbContextLib\DbContextLib.csproj", "{330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbContextLib", "DbContextLib\DbContextLib.csproj", "{330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entities", "Entities\Entities.csproj", "{A07F86B3-555B-4B35-8BB1-25E844825A38}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entities", "Entities\Entities.csproj", "{A07F86B3-555B-4B35-8BB1-25E844825A38}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubbedContextLib", "StubbedContextLib\StubbedContextLib.csproj", "{2F44DE6E-EFFC-42FE-AFF6-79CDC762E6D8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubbedContextLib", "StubbedContextLib\StubbedContextLib.csproj", "{2F44DE6E-EFFC-42FE-AFF6-79CDC762E6D8}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestEntities", "Tests\ConsoleTestEntities\ConsoleTestEntities.csproj", "{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestEntities", "Tests\ConsoleTestEntities\ConsoleTestEntities.csproj", "{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestRelationships", "Tests\ConsoleTestRelationships\ConsoleTestRelationships.csproj", "{2D166FAD-4934-474B-96A8-6C0635156EC2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestRelationships", "Tests\ConsoleTestRelationships\ConsoleTestRelationships.csproj", "{2D166FAD-4934-474B-96A8-6C0635156EC2}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dto", "Dto\Dto.csproj", "{562019BC-0F61-41B0-9BAE-259B92C6BFBA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dto", "Dto\Dto.csproj", "{562019BC-0F61-41B0-9BAE-259B92C6BFBA}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HeartTrackAPI", "HeartTrackAPI\HeartTrackAPI.csproj", "{C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeartTrackAPI", "HeartTrackAPI\HeartTrackAPI.csproj", "{C1C2EAC3-3347-466B-BFB6-2A9F11A3AE12}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestsAPI", "TestsAPI", "{30FC2BE9-7397-445A-84AD-043CE70F4281}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestsAPI", "TestsAPI", "{30FC2BE9-7397-445A-84AD-043CE70F4281}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestsEntities", "Tests\UnitTestsEntities\UnitTestsEntities.csproj", "{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model2Entities", "Model2Entities\Model2Entities.csproj", "{FA329DEF-4756-4A8B-84E9-5A625FF94CBF}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsEntities", "Tests\UnitTestsEntities\UnitTestsEntities.csproj", "{31FA8E5E-D642-4C43-A2B2-02B9832B2CEC}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubAPI", "StubAPI\StubAPI.csproj", "{C9BD0310-DC18-4356-B8A7-2B6959AF7813}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model2Entities", "Model2Entities\Model2Entities.csproj", "{FA329DEF-4756-4A8B-84E9-5A625FF94CBF}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestEFMapper", "Tests\ConsoleTestEFMapper\ConsoleTestEFMapper.csproj", "{73EA27F2-9F0C-443F-A5EE-2960C983A422}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubAPI", "StubAPI\StubAPI.csproj", "{C9BD0310-DC18-4356-B8A7-2B6959AF7813}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFMappers", "EFMappers\EFMappers.csproj", "{9397795D-F482-44C4-8443-A20AC26671AA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTestEFMapper", "Tests\ConsoleTestEFMapper\ConsoleTestEFMapper.csproj", "{73EA27F2-9F0C-443F-A5EE-2960C983A422}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "APIMappers", "APIMappers\APIMappers.csproj", "{41D18203-1688-43BD-A3AC-FD0C2BD81909}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFMappers", "EFMappers\EFMappers.csproj", "{9397795D-F482-44C4-8443-A20AC26671AA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsModel", "Tests\UnitTestsModel\UnitTestsModel.csproj", "{508D380F-145C-437E-A7DF-7A17C526B2F3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "APIMappers", "APIMappers\APIMappers.csproj", "{41D18203-1688-43BD-A3AC-FD0C2BD81909}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entities2Dto", "Entities2Dto\Entities2Dto.csproj", "{1B15D383-1DFA-47E8-86EC-AC631B15FBEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RepositoriesUnitTest", "Tests\RepositoriesUnitTest\RepositoriesUnitTest.csproj", "{707B1AC4-F896-4270-BC2F-1A589F48979D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebAPIConsoleTests", "Tests\WebAPIConsoleTests\WebAPIConsoleTests.csproj", "{D0EE112F-3151-4C28-A6EC-B1CEC7883FAE}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}.Debug|Any CPU.Build.0 = Debug|Any CPU {330A5DA0-0B4E-48D4-9AF3-6DCB39EE9622}.Debug|Any CPU.Build.0 = Debug|Any CPU
@ -85,10 +82,22 @@ Global
{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{F80C60E1-1E06-46C2-96DE-42B1C7DE65BC}.Release|Any CPU.Build.0 = 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
{30AB7FAA-6072-40B6-A15E-9188B59144F9}.Debug|Any CPU.ActiveCfg = Debug|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}.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.ActiveCfg = Release|Any CPU
{30AB7FAA-6072-40B6-A15E-9188B59144F9}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
@ -105,10 +114,18 @@ Global
{C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Debug|Any CPU.Build.0 = Debug|Any CPU {C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Release|Any CPU.ActiveCfg = Release|Any CPU {C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Release|Any CPU.Build.0 = Release|Any CPU {C9BD0310-DC18-4356-B8A7-2B6959AF7813}.Release|Any CPU.Build.0 = Release|Any CPU
{06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06DBE9E4-6AA5-4D09-8544-D3ED91E2D980}.Release|Any CPU.Build.0 = Release|Any CPU
{73EA27F2-9F0C-443F-A5EE-2960C983A422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73EA27F2-9F0C-443F-A5EE-2960C983A422}.Debug|Any CPU.Build.0 = Debug|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73EA27F2-9F0C-443F-A5EE-2960C983A422}.Release|Any CPU.ActiveCfg = Release|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73EA27F2-9F0C-443F-A5EE-2960C983A422}.Release|Any CPU.Build.0 = Release|Any CPU {73EA27F2-9F0C-443F-A5EE-2960C983A422}.Release|Any CPU.Build.0 = Release|Any CPU
{C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9C9F2A5-9132-4067-B240-B299D2FCF4E9}.Release|Any CPU.Build.0 = Release|Any CPU
{9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.Build.0 = Debug|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9397795D-F482-44C4-8443-A20AC26671AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {9397795D-F482-44C4-8443-A20AC26671AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -117,31 +134,14 @@ Global
{41D18203-1688-43BD-A3AC-FD0C2BD81909}.Debug|Any CPU.Build.0 = Debug|Any CPU {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41D18203-1688-43BD-A3AC-FD0C2BD81909}.Release|Any CPU.ActiveCfg = Release|Any CPU {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41D18203-1688-43BD-A3AC-FD0C2BD81909}.Release|Any CPU.Build.0 = Release|Any CPU {41D18203-1688-43BD-A3AC-FD0C2BD81909}.Release|Any CPU.Build.0 = Release|Any CPU
{508D380F-145C-437E-A7DF-7A17C526B2F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{508D380F-145C-437E-A7DF-7A17C526B2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{508D380F-145C-437E-A7DF-7A17C526B2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{508D380F-145C-437E-A7DF-7A17C526B2F3}.Release|Any CPU.Build.0 = Release|Any CPU
{1B15D383-1DFA-47E8-86EC-AC631B15FBEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B15D383-1DFA-47E8-86EC-AC631B15FBEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{707B1AC4-F896-4270-BC2F-1A589F48979D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{707B1AC4-F896-4270-BC2F-1A589F48979D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0EE112F-3151-4C28-A6EC-B1CEC7883FAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0EE112F-3151-4C28-A6EC-B1CEC7883FAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {477D2129-A6C9-4FF8-8BE9-5E9E8E5282F8} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{2D166FAD-4934-474B-96A8-6C0635156EC2} = {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} {30FC2BE9-7397-445A-84AD-043CE70F4281} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
{E515C8B6-6282-4D8B-8523-7B3A13E4AF58} = {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} {31FA8E5E-D642-4C43-A2B2-02B9832B2CEC} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{73EA27F2-9F0C-443F-A5EE-2960C983A422} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18} {73EA27F2-9F0C-443F-A5EE-2960C983A422} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{508D380F-145C-437E-A7DF-7A17C526B2F3} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{D0EE112F-3151-4C28-A6EC-B1CEC7883FAE} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0F3487F4-66CA-4034-AC66-1BC899C9B523}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

@ -1,13 +1,12 @@
using System.Reflection; using System.Reflection;
using DbContextLib; using DbContextLib;
using Entities; using DbContextLib.Identity;
using HeartTrackAPI.Services; using HeartTrackAPI.Utils;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Model.Manager; using Model.Manager;
using Model2Entities; using Model2Entities;
@ -15,7 +14,7 @@ using StubAPI;
using StubbedContextLib; using StubbedContextLib;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
namespace HeartTrackAPI.Utils; namespace HeartTrackAPI;
public class AppBootstrap(IConfiguration configuration) public class AppBootstrap(IConfiguration configuration)
{ {
@ -24,14 +23,14 @@ public class AppBootstrap(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddControllers(); services.AddControllers();
services.AddEndpointsApiExplorer(); services.AddEndpointsApiExplorer();
AddSwagger(services); AddSwagger(services);
AddHeartTrackContextServices(services); AddHeartTrackContextServices(services);
AddModelService(services); AddModelService(services);
AddIdentityServices(services, configuration); AddIdentityServices(services);
AddApiVersioning(services); AddApiVersioning(services);
services.AddHealthChecks(); services.AddHealthChecks();
} }
private void AddHeartTrackContextServices(IServiceCollection services) private void AddHeartTrackContextServices(IServiceCollection services)
@ -50,7 +49,9 @@ public class AppBootstrap(IConfiguration configuration)
connectionString = $"Server={host};port={port};database={database};user={username};password={password}"; connectionString = $"Server={host};port={port};database={database};user={username};password={password}";
Console.WriteLine("========RUNNING USING THE MYSQL SERVER=============="); Console.WriteLine("========RUNNING USING THE MYSQL SERVER==============");
Console.WriteLine(connectionString); Console.WriteLine(connectionString);
Console.WriteLine("===================================================="); Console.WriteLine(connectionString);
Console.WriteLine("======================");
services.AddDbContext<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb"));
services.AddDbContext<HeartTrackContext>(options => services.AddDbContext<HeartTrackContext>(options =>
options.UseMySql($"{connectionString}", new MySqlServerVersion(new Version(10, 11, 1))) options.UseMySql($"{connectionString}", new MySqlServerVersion(new Version(10, 11, 1)))
, ServiceLifetime.Singleton); , ServiceLifetime.Singleton);
@ -58,18 +59,19 @@ public class AppBootstrap(IConfiguration configuration)
default: default:
Console.WriteLine("====== RUNNING USING THE IN SQLITE DATABASE ======"); Console.WriteLine("====== RUNNING USING THE IN SQLITE DATABASE ======");
connectionString = Configuration.GetConnectionString("HeartTrackAuthConnection"); connectionString = Configuration.GetConnectionString("HeartTrackAuthConnection");
Console.WriteLine(connectionString);
if (!string.IsNullOrWhiteSpace(connectionString)) if (!string.IsNullOrWhiteSpace(connectionString))
{ {
services.AddDbContext<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb"));
services.AddDbContext<TrainingStubbedContext>(options => services.AddDbContext<TrainingStubbedContext>(options =>
options.UseSqlite(connectionString), ServiceLifetime.Singleton); options.UseSqlite(connectionString), ServiceLifetime.Singleton);
} }
else else
{ {
services.AddDbContext<AuthDbContext>(options => options.UseInMemoryDatabase("AuthDb"));
services.AddDbContext<TrainingStubbedContext>(); services.AddDbContext<TrainingStubbedContext>();
} }
break; break;
} }
} }
@ -78,10 +80,9 @@ public class AppBootstrap(IConfiguration configuration)
switch (Environment.GetEnvironmentVariable("TYPE")) switch (Environment.GetEnvironmentVariable("TYPE"))
{ {
case "BDD": case "BDD":
services.AddSingleton<IDataManager>(provider => services.AddSingleton<IDataManager>(provider => new DbDataManager(provider.GetRequiredService<HeartTrackContext>()));
new DbDataManager(provider.GetRequiredService<HeartTrackContext>()));
break; break;
case "STUB-MODEL": case "STUB":
services.AddSingleton<IDataManager, StubData>(); services.AddSingleton<IDataManager, StubData>();
break; break;
default: default:
@ -90,80 +91,25 @@ public class AppBootstrap(IConfiguration configuration)
provider.GetRequiredService<TrainingStubbedContext>().Database.EnsureCreated(); provider.GetRequiredService<TrainingStubbedContext>().Database.EnsureCreated();
return new DbDataManager(provider.GetRequiredService<TrainingStubbedContext>()); return new DbDataManager(provider.GetRequiredService<TrainingStubbedContext>());
}); });
// services.AddSingleton<IDataManager>(provider => new DbDataManager(provider.GetRequiredService<TrainingStubbedContext>()));
break; break;
} }
// Auth
services.AddScoped<ITokenService, TokenService>();
}
private void AddIdentityServices(IServiceCollection services, IConfiguration config)
{
/*services.AddAuthorization();
services.AddIdentityApiEndpoints<AthleteEntity>()
.AddEntityFrameworkStores<TrainingStubbedContext>();*/
var identityBuilder = services.AddIdentity<AthleteEntity, IdentityRole<int>>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequiredLength = 8;
});
if (Environment.GetEnvironmentVariable("TYPE") == "BDD") //services.AddTransient<IActivityManager, ActivityManager>();
{
identityBuilder.AddEntityFrameworkStores<HeartTrackContext>();
}
else
{
identityBuilder.AddEntityFrameworkStores<TrainingStubbedContext>();
} }
services.AddAuthentication(options => private void AddIdentityServices(IServiceCollection services)
{
options.DefaultAuthenticateScheme =
options.DefaultChallengeScheme =
options.DefaultForbidScheme =
options.DefaultScheme =
options.DefaultSignInScheme =
options.DefaultSignOutScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{ {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer =config["JWT:Issuer"],
ValidateAudience = true,
ValidAudience = config["JWT:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
System.Text.Encoding.UTF8.GetBytes(config["JWT:SigningKey"])
)
};
});
/*
app.UseCors(x => x
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
//.WithOrigins("https://localhost:44351))
.SetIsOriginAllowed(origin => true));*/
// services.AddTransient<IEmailSender<AthleteEntity>, EmailSender>(); // services.AddTransient<IEmailSender<AthleteEntity>, EmailSender>();
// services.AddAuthorization(); services.AddAuthorization();
// services.AddIdentityApiEndpoints<AthleteEntity>() services.AddIdentityApiEndpoints<IdentityUser>()
// .AddEntityFrameworkStores<HeartTrackContext>(); .AddEntityFrameworkStores<AuthDbContext>();
// .AddEntityFrameworkStores<AuthDbContext>().AddDefaultTokenProviders(); // .AddEntityFrameworkStores<AuthDbContext>().AddDefaultTokenProviders();
} }
private void AddApiVersioning(IServiceCollection services) private void AddApiVersioning(IServiceCollection services)
{ {
services.AddApiVersioning(opt => services.AddApiVersioning(opt =>
{ {
opt.ReportApiVersions = true; opt.ReportApiVersions = true;
@ -173,12 +119,14 @@ public class AppBootstrap(IConfiguration configuration)
new HeaderApiVersionReader("x-api-version"), new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("x-api-version")); new MediaTypeApiVersionReader("x-api-version"));
}); });
}
}
private void AddSwagger(IServiceCollection services) private void AddSwagger(IServiceCollection services)
{ {
services.AddSwaggerGen(options => services.AddSwaggerGen(options =>
{ {
options.OperationFilter<SwaggerDefaultValues>();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath); options.IncludeXmlComments(xmlPath);
@ -189,9 +137,7 @@ public class AppBootstrap(IConfiguration configuration)
"JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization", Name = "Authorization",
In = ParameterLocation.Header, In = ParameterLocation.Header,
Type = SecuritySchemeType.Http, Type = SecuritySchemeType.ApiKey
Scheme = "Bearer",
BearerFormat = "JWT"
}); });
var scheme = new OpenApiSecurityRequirement var scheme = new OpenApiSecurityRequirement
{ {
@ -210,62 +156,50 @@ public class AppBootstrap(IConfiguration configuration)
new List<string>() new List<string>()
} }
}; };
options.AddSecurityRequirement(scheme); options.AddSecurityRequirement(scheme);
options.OperationFilter<SwaggerDefaultValues>();
}); });
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerOptions>(); services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerOptions>();
services.AddSwaggerGen(options =>
{
options.OperationFilter<SwaggerDefaultValues>();
});
services.AddVersionedApiExplorer(setup => services.AddVersionedApiExplorer(setup =>
{ {
setup.GroupNameFormat = "'v'VVV"; setup.GroupNameFormat = "'v'VVV";
setup.SubstituteApiVersionInUrl = true; setup.SubstituteApiVersionInUrl = true;
}); });
} }
public void Configure(WebApplication app, IWebHostEnvironment env) public void Configure(WebApplication app, IWebHostEnvironment env)
{ {
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.MapIdentityApi<IdentityUser>();
app.UseAuthentication();
app.UseAuthorization();
// app.MapIdentityApi<AthleteEntity>();
app.MapControllers(); app.MapControllers();
app.UseAuthorization();
app.MapHealthChecks("/health"); app.MapHealthChecks("/health");
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (true) if (true)
{ {
app.UseSwagger(options => var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
{ app.UseSwagger();
options.PreSerializeFilters.Add((swagger, httpReq) => app.UseSwaggerUI();
{ app.MapSwagger();
if (httpReq.Headers.ContainsKey("X-Forwarded-Host")) app.UseSwaggerUI(options =>
{ {
string basePath; foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
switch (Environment.GetEnvironmentVariable("TYPE")) // httpReq.Host.Value
{ {
case "STUB": options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
basePath = "containers/HeartDev-heart_stub"; description.GroupName.ToUpperInvariant());
break;
case "BDD":
basePath = "containers/HeartDev-api";
break;
default:
basePath = httpReq.Host.Value;
break;
} }
var serverUrl = $"https://{httpReq.Headers["X-Forwarded-Host"]}/{basePath}";
swagger.Servers = new List<OpenApiServer> { new() { Url = serverUrl } };
}
});
}); });
app.UseSwaggerUI();
app.MapSwagger();
} }
} }
} }

@ -1,10 +1,9 @@
using APIMappers; using APIMappers;
using Dto; using Dto;
using Dto.Tiny;
using HeartTrackAPI.Request; using HeartTrackAPI.Request;
using HeartTrackAPI.Responce; using HeartTrackAPI.Responce;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Model;
using Shared; using Shared;
using Model.Manager; using Model.Manager;
using Model.Repository; using Model.Repository;
@ -13,29 +12,22 @@ namespace HeartTrackAPI.Controllers;
[ApiController] [ApiController]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[Authorize]
public class ActivityController : Controller public class ActivityController : Controller
{ {
private readonly IActivityRepository _activityService; private readonly IActivityRepository _activityService;
private readonly ILogger<ActivityController> _logger; private readonly ILogger<ActivityController> _logger;
private readonly IUserRepository _userRepository;
private readonly IDataSourceRepository<DataSourceTinyDto> _dataSourceRepository;
public ActivityController(IDataManager dataManager, ILogger<ActivityController> logger) public ActivityController(IDataManager dataManager, ILogger<ActivityController> logger)
{ {
_activityService = dataManager.ActivityRepo; _activityService = dataManager.ActivityRepo;
_userRepository = dataManager.UserRepo;
_dataSourceRepository = dataManager.DataSourceRepo;
_logger = logger; _logger = logger;
} }
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(PageResponse<ActivityTinyDto>), 200)] [ProducesResponseType(typeof(PageResponse<ActivityDto>), 200)]
[ProducesResponseType(400)] [ProducesResponseType(400)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<ActivityTinyDto>>> GetActivities([FromQuery] PageRequest pageRequest) public async Task<ActionResult<PageResponse<ActivityDto>>> GetActivities([FromQuery] PageRequest pageRequest)
{ {
try try
{ {
@ -46,14 +38,12 @@ public class ActivityController : Controller
return BadRequest("To many object is asked the max is : " + totalCount); return BadRequest("To many object is asked the max is : " + totalCount);
} }
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivities), pageRequest); _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivities), pageRequest);
var activities = await _activityService.GetActivities(pageRequest.Index, pageRequest.Count, Enum.TryParse(pageRequest.OrderingPropertyName, out ActivityOrderCriteria result) ? result : ActivityOrderCriteria.None, pageRequest.Descending ?? false); var activities = await _activityService.GetActivities(pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false);
if(activities == null) if(activities == null)
{ {
return NotFound("No activities found"); return NotFound("No activities found");
} }
var pageResponse = new PageResponse<ActivityDto>(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto()));
var pageResponse =
new PageResponse<ActivityTinyDto>(pageRequest.Index, pageRequest.Count, totalCount, activities);
return Ok(pageResponse); return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
@ -64,110 +54,106 @@ public class ActivityController : Controller
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> PostActivity(NewActivityDto activityDto) public async Task<IActionResult> PostActivity(ActivityDto activityDto)
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters}, {add}", nameof(PostActivity), activityDto.Activity.Average, activityDto.HeartRates[0].Timestamp); var activity = activityDto.ToModel();
var user = await _userRepository.GetUserTinyById(activityDto.AthleteId); var result = await _activityService.AddActivity(activity);
if (user == null) if (result == null)
{ {
_logger.LogError("Athlete with id {id} not found", activityDto.AthleteId); return BadRequest();
return NotFound($"Athlete with id {activityDto.AthleteId} not found"); }
return CreatedAtAction(nameof(GetActivity), new { id = result.Id }, result.ToDto());
} }
if (activityDto.DataSourceId != null)
{
var dataSource = await _dataSourceRepository.GetItemById(activityDto.DataSourceId.Value);
if (dataSource == null) /*
public async Task<IActionResult> PostFitFile( Stream file, string contentType) // [FromForm]
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{ {
_logger.LogError("DataSource with id {id} not found", activityDto.DataSourceId); ModelState.AddModelError("File",
return NotFound($"DataSource with id {activityDto.DataSourceId} not found"); $"The request couldn't be processed (Error 1).");
// Log error
return BadRequest(ModelState);
} }
if (file == null)
{
return BadRequest("No file was provided");
} }
//var fileUploadSummary = await _fileService.UploadFileAsync(HttpContext.Request.Body, Request.ContentType);
var result = await _activityService.AddActivity(activityDto); // var activity = await _activityManager.AddActivityFromFitFile(file);
var activity = new Activity
if (result == null)
{ {
return BadRequest(); Id = 1,
Type = "Running",
Date = new DateTime(2021, 10, 10),
StartTime = new DateTime(2021, 10, 10, 10, 0, 0),
EndTime = new DateTime(2021, 10, 10, 11, 0, 0),
Effort = 3,
Variability = 0.5f,
Variance = 0.5f,
StandardDeviation = 0.5f,
Average = 5.0f,
Maximum = 10,
Minimum = 0,
AverageTemperature = 20.0f,
HasAutoPause = false,
Users =
{
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()
} }
_logger.LogInformation("Activity added with id {id}", result.Id);
return CreatedAtAction(nameof(GetActivity), new { id = result.Id }, result);
} }
};
if (activity == null)
{
return BadRequest("The file provided is not a valid fit file");
}
return CreatedAtAction(nameof(GetActivity), new { id = activity.Id }, activity.ToDto());
}*/
[HttpGet("{id}")] [HttpGet("{id}")]
public async Task<ActionResult<ResponseActivityDto>> GetActivity(int id) public async Task<ActionResult<ActivityDto>> GetActivity(int id)
{ {
try var activity = await _activityService.GetActivityByIdAsync(id);
{
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivity), id);
var activity = await _activityService.GetActivityById(id);
if (activity == null) if (activity == null)
{ {
_logger.LogError("Activity with id {id} not found", id); return NotFound();
return NotFound($"Activity with id {id} not found");
}
return Ok(activity);
}
catch (Exception e)
{
_logger.LogError(e, "Error while getting activity by id {id}", id);
return Problem();
} }
return Ok(activity.ToDto());
} }
[HttpPut("{id}")] [HttpPut("{id}")]
public async Task<IActionResult> PutActivity(int id, ActivityTinyDto activityDto) public async Task<IActionResult> PutActivity(int id, ActivityDto activityDto)
{ {
var result = await _activityService.UpdateActivity(id, activityDto); if (id != activityDto.Id)
{
return BadRequest();
}
var activity = activityDto.ToModel();
var result = await _activityService.UpdateActivity(id, activity);
if (result == null) if (result == null)
{ {
return NotFound(); return NotFound();
} }
return NoContent();
return Ok(result);
} }
/// <summary>
/// Supprime une activity spécifique.
/// </summary>
/// <param name="id">L'identifiant de l'activity à supprimer.</param>
/// <returns>Action result.</returns>
/// <response code="200">Activity supprimé avec succès.</response>
/// <response code="404">Activity non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpDelete("{id}")] [HttpDelete("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<IActionResult> DeleteActivity(int id) public async Task<IActionResult> DeleteActivity(int id)
{ {
try var result = await _activityService.DeleteActivity(id);
if (!result)
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(DeleteActivity), null,id); return NotFound();
var activity = await _activityService.GetActivityByIdAsync(id);
if (activity == null)
{
_logger.LogError("Activity with id {id} not found", id);
return NotFound($"Activity with id {id} not found");
}
var isDeleted = await _activityService.DeleteActivity(id);
if(!isDeleted)
{
_logger.LogError("Error while deleting activity with id {id}", id);
return Problem("Error while deleting activity");
}
return Ok();
}
catch (Exception e)
{
_logger.LogError(e, "Error while deleting activity");
return Problem();
} }
return NoContent();
} }
/* /*

@ -1,116 +0,0 @@
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());
}
*/

@ -1,154 +0,0 @@
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();
}*/
}

@ -1,11 +1,8 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using APIMappers; using APIMappers;
using Dto; using Dto;
using Dto.Tiny;
using HeartTrackAPI.Request; using HeartTrackAPI.Request;
using HeartTrackAPI.Responce; using HeartTrackAPI.Responce;
using HeartTrackAPI.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Model.Manager; using Model.Manager;
@ -20,7 +17,6 @@ namespace HeartTrackAPI.Controllers;
[ApiController] [ApiController]
[ApiVersion("1.0")] [ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[Authorize]
public class UsersController : Controller public class UsersController : Controller
{ {
private readonly ILogger<UsersController> _logger; private readonly ILogger<UsersController> _logger;
@ -36,16 +32,16 @@ public class UsersController : Controller
/// <summary> /// <summary>
/// Récupère une page d'utilisateurs en fonction des critères de pagination et de tri fournis. /// Récupère une page d'utilisateurs en fonction des critères de pagination et de tri fournis.
/// </summary> /// </summary>
/// <param name="request">Les critères de pagination et de tri pour les utilisateurs. Met None par defaut et/ou si le critère n'est pas correct</param> /// <param name="request">Les critères de pagination et de tri pour les utilisateurs.</param>
/// <returns>Une page de données utilisateur selon les critères spécifiés.</returns> /// <returns>Une page de données utilisateur selon les critères spécifiés.</returns>
/// <response code="200">Retourne la page demandée d'utilisateurs.</response> /// <response code="200">Retourne la page demandée d'utilisateurs.</response>
/// <response code="400">La demande de pagination est invalide.</response> /// <response code="400">La demande de pagination est invalide.</response>
/// <response code="500">Erreur interne du serveur.</response> /// <response code="500">Erreur interne du serveur.</response>
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(PageResponse<UserTinyDto>), 200)] [ProducesResponseType(typeof(PageResponse<UserDto>), 200)]
[ProducesResponseType(400)] [ProducesResponseType(400)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserTinyDto>>> Get([FromQuery] PageRequest request) public async Task<ActionResult<PageResponse<UserDto>>> Get([FromQuery] PageRequest request)
{ {
try try
{ {
@ -58,8 +54,8 @@ public class UsersController : Controller
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), null); _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), null);
var athletes = await _userService.GetUsersTiny(request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : 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<UserTinyDto>(request.Index, request.Count, totalCount, athletes); var pageResponse = new PageResponse<UserDto>(request.Index, request.Count, totalCount, athletes!.Select(a => a.ToDto()));
return Ok(pageResponse); return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
@ -78,21 +74,21 @@ public class UsersController : Controller
/// <response code="404">Aucun utilisateur trouvé pour l'identifiant spécifié.</response> /// <response code="404">Aucun utilisateur trouvé pour l'identifiant spécifié.</response>
/// <response code="500">Erreur interne du serveur.</response> /// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{id}")] [HttpGet("{id}")]
[ProducesResponseType(typeof(ResponseUserDto), 200)] [ProducesResponseType(typeof(UserDto), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<ResponseUserDto>> GetById([Range(0,int.MaxValue)]int id) public async Task<ActionResult<UserDto>> GetById([Range(0,int.MaxValue)]int id)
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetById), id); _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetById), id);
var athlete = await _userService.GetUserById(id); var athlete = await _userService.GetItemById(id);
if (athlete == null) if (athlete == null)
{ {
_logger.LogError("Athlete with id {id} not found", id); _logger.LogError("Athlete with id {id} not found", id);
return NotFound($"Athlete with id {id} not found"); return NotFound($"Athlete with id {id} not found");
} }
return Ok(athlete); return Ok(athlete.ToDto());
} }
catch (Exception e) catch (Exception e)
{ {
@ -114,7 +110,7 @@ public class UsersController : Controller
{ {
try try
{ {
_logger.LogInformation("Executing {Action}", nameof(Count)); _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Count), null);
var nbUsers = await _userService.GetNbItems(); var nbUsers = await _userService.GetNbItems();
return Ok(nbUsers); return Ok(nbUsers);
} }
@ -135,27 +131,27 @@ public class UsersController : Controller
/// <response code="404">Utilisateur non trouvé.</response> /// <response code="404">Utilisateur non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response> /// <response code="500">Erreur interne du serveur.</response>
[HttpPut("{id}")] [HttpPut("{id}")]
[ProducesResponseType(typeof(UserTinyDto), 200)] [ProducesResponseType(typeof(UserDto), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<UserTinyDto>> Update(int id, [FromBody] UserTinyDto user) public async Task<ActionResult<UserDto>> Update(int id, [FromBody] UserDto user)
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Update), user,id); _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Update), user,id);
var athlete = await _userService.GetUserTinyById(id); var athlete = await _userService.GetItemById(id);
if (athlete == null) if (athlete == null)
{ {
_logger.LogError("Athlete with id {id} not found", id); _logger.LogError("Athlete with id {id} not found", id);
return NotFound($"Athlete with id {id} not found"); return NotFound($"Athlete with id {id} not found");
} }
var updatedAthlete = await _userService.UpdateUser(id, user); var updatedAthlete = await _userService.UpdateItem(id, user.ToModel());
if(updatedAthlete == null) if(updatedAthlete == null)
{ {
_logger.LogError("Error while updating athlete with id {id}", id); _logger.LogError("Error while updating athlete with id {id}", id);
return Problem(); return Problem();
} }
return Ok(updatedAthlete); return Ok(updatedAthlete.ToDto());
} }
catch (Exception e) catch (Exception e)
@ -183,7 +179,8 @@ public class UsersController : Controller
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Delete), null,id); _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Delete), null,id);
var athlete = await _userService.GetUserTinyById(id);
var athlete = await _userService.GetItemById(id);
if (athlete == null) if (athlete == null)
{ {
_logger.LogError("Athlete with id {id} not found", id); _logger.LogError("Athlete with id {id} not found", id);
@ -214,30 +211,31 @@ public class UsersController : Controller
/// <response code="404">Utilisateur non trouvé.</response> /// <response code="404">Utilisateur non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response> /// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{id}/friends")] [HttpGet("{id}/friends")]
[ProducesResponseType(typeof(PageResponse<UserTinyDto>), 200)] [ProducesResponseType(typeof(PageResponse<UserDto>), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserTinyDto>>> GetFriends(int id, [FromQuery] PageRequest request) public async Task<ActionResult<PageResponse<UserDto>>> GetFriends(int id, [FromQuery] PageRequest request)
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetFriends), null,id); _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetFriends), null,id);
var totalCount = await _userService.GetNbFriends(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 totalCount = await _userService.GetNbFriends(athlete);
if (request.Count * request.Index >= totalCount) if (request.Count * request.Index >= totalCount)
{ {
_logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", 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); return BadRequest("To many object is asked the max is : " + totalCount);
} }
var friends = await _userService.GetFriends(id, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); var friends = await _userService.GetFriends(athlete, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false);
if (friends == null) return NotFound(); if (friends == null) return NotFound();
var pageResponse = new PageResponse<UserTinyDto>(request.Index, request.Count, totalCount, friends); var pageResponse = new PageResponse<UserDto>(request.Index, request.Count, totalCount, friends.Select(a => a.ToDto()));
return Ok(pageResponse); return Ok(pageResponse);
} }
catch(ModelNotFoundException e)
{
_logger.LogError(e, "Error while adding a friend to an athlete");
return BadRequest(e.Message);
}
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while getting the number of users"); _logger.LogError(e, "Error while getting the number of users");
@ -258,13 +256,24 @@ public class UsersController : Controller
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<IActionResult> AddFollowing(int id, int friendId) public async Task<IActionResult> AddFriend(int id, int friendId)
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(AddFollowing), friendId,id); _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(AddFriend), friendId,id);
var athlete = await _userService.GetItemById(id);
var isAdded = await _userService.AddFollowing(id, friendId); if (athlete == null)
{
_logger.LogError("Athlete with id {id} not found", id);
return NotFound($"Athlete with id {id} not found");
}
var friend = await _userService.GetItemById(friendId);
if (friend == null)
{
_logger.LogError("Athlete with id {id} not found", friendId);
return NotFound($"Athlete with id {friendId} not found");
}
var isAdded = await _userService.AddFriend(athlete, friend);
if(!isAdded) if(!isAdded)
{ {
_logger.LogError("Error while adding friend with id {friendId} to athlete with id {id}", friendId, id); _logger.LogError("Error while adding friend with id {friendId} to athlete with id {id}", friendId, id);
@ -272,14 +281,9 @@ public class UsersController : Controller
} }
return Ok(); return Ok();
} }
catch(FriendShipException e)
{
_logger.LogError(e, "Error while adding a friend to an athlete");
return BadRequest(e.Message);
}
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while attempting to follow a user"); _logger.LogError(e, "Error while getting the number of users");
return Problem(); return Problem();
} }
} }
@ -303,8 +307,19 @@ public class UsersController : Controller
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(RemoveFriend), friendId,id); _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(RemoveFriend), friendId,id);
var athlete = await _userService.GetItemById(id);
var isRemoved = await _userService.RemoveFollowing(id, friendId); if (athlete == null)
{
_logger.LogError("Athlete with id {id} not found", id);
return NotFound($"Athlete with id {id} not found");
}
var friend = await _userService.GetItemById(friendId);
if (friend == null)
{
_logger.LogError("Athlete with id {id} not found", friendId);
return NotFound($"Athlete with id {friendId} not found");
}
var isRemoved = await _userService.RemoveFriend(athlete, friend);
if(!isRemoved) if(!isRemoved)
{ {
_logger.LogError("Error while removing friend with id {friendId} to athlete with id {id}", friendId, id); _logger.LogError("Error while removing friend with id {friendId} to athlete with id {id}", friendId, id);
@ -312,19 +327,57 @@ public class UsersController : Controller
} }
return Ok(); return Ok();
} }
catch(FriendShipException e) catch (Exception e)
{
_logger.LogError(e, "Error while getting the number of users");
return Problem();
}
}
// #[TODO] [Dave] ou faire un get qui si le role est coach resend les athletes et si le role est athlete resend les coach
/// <summary>
/// Obtient la liste des athlètes d'un coach spécifique.
/// </summary>
/// <param name="coachId">L'identifiant du coach.</param>
/// <param name="request">Les critères de pagination et de tri.</param>
/// <returns>La liste paginée des athlètes.</returns>
/// <response code="200">Retourne la liste paginée des athlètes du coach.</response>
/// <response code="404">Coach non trouvé.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{coachId}/athletes")]
[ProducesResponseType(typeof(PageResponse<UserDto>), 200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserDto>>> GetAthletes(int coachId, [FromQuery] PageRequest request)
{
try
{
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetAthletes), null,coachId);
var coach = await _userService.GetItemById(coachId);
if (coach == null)
{ {
_logger.LogError(e, "Error while removing a friend to an athlete"); _logger.LogError("Athlete with id {id} not found", coachId);
return BadRequest(e.Message); return NotFound($"Athlete with id {coachId} not found");
}
var totalCount = await _userService.GetNbFriends(coach);
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);
}
var athletes = await _userService.GetFriends(coach, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false);
if (athletes == null) return NotFound();
var pageResponse = new PageResponse<UserDto>(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto()));
return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "Error while attempting to unfollow a user"); _logger.LogError(e, "Error while getting the number of users");
return Problem(); return Problem();
} }
} }
/*
/// <summary> /// <summary>
/// Obtient la liste des activités d'un utilisateur spécifique. /// Obtient la liste des activités d'un utilisateur spécifique.
/// </summary> /// </summary>
@ -336,7 +389,7 @@ public class UsersController : Controller
/// <response code="500">Erreur interne du serveur.</response> /// <response code="500">Erreur interne du serveur.</response>
[HttpGet("{userId}/activities")] [HttpGet("{userId}/activities")]
// should be tiny DTOActivity returned with only the necessary information (will be used in the list of activities of a user) // should be tiny DTOActivity returned with only the necessary information (will be used in the list of activities of a user)
public async Task<ActionResult<PageResponse<ActivityTinyDto>>> GetActivitiesByUser(int userId, [FromQuery] PageRequest pageRequest) public async Task<ActionResult<PageResponse<ActivityDto>>> GetActivitiesByUser(int userId, [FromQuery] PageRequest pageRequest)
{ {
try try
{ {
@ -347,12 +400,12 @@ public class UsersController : Controller
return BadRequest("To many object is asked the max is : " + totalCount); return BadRequest("To many object is asked the max is : " + totalCount);
} }
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivitiesByUser), pageRequest); _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivitiesByUser), pageRequest);
var activities = await _activityService.GetActivitiesByUser(userId, pageRequest.Index, pageRequest.Count, Enum.TryParse(pageRequest.OrderingPropertyName, out ActivityOrderCriteria result) ? result : ActivityOrderCriteria.None, pageRequest.Descending ?? false); var activities = await _activityService.GetActivitiesByUser(userId, pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false);
if(activities == null) if(activities == null)
{ {
return NotFound("No activities found"); return NotFound("No activities found");
} }
var pageResponse = new PageResponse<ActivityTinyDto>(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto())); var pageResponse = new PageResponse<ActivityDto>(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto()));
return Ok(pageResponse); return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
@ -360,7 +413,33 @@ public class UsersController : Controller
_logger.LogError(e, "Error while getting all activities"); _logger.LogError(e, "Error while getting all activities");
return Problem(); return Problem();
} }
}*/ }
/* [TODO] [Dave]
[HttpGet("{userId}/trainings")]
[ProducesResponseType(typeof(PageResponse<TrainingDto>), 200)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<TrainingDto>> GetTrainings(int userId, [FromQuery] PageRequest request)
*/
/// <summary>
/// Déconnecte l'utilisateur actuel.
/// </summary>
/// <param name="signInManager">Le gestionnaire de connexion.</param>
/// <param name="empty">Paramètre vide utilisé pour s'assurer que la requête provient bien d'un client.</param>
/// <returns>Action result.</returns>
/// <response code="200">Déconnexion réussie.</response>
/// <response code="401">Déconnexion non autorisée.</response>
/// <response code="500">Erreur interne du serveur.</response>
[HttpPost("logout")]
[ProducesResponseType(200)]
[ProducesResponseType(401)]
[ProducesResponseType(500)]
public async Task<IActionResult> Logout(SignInManager<IdentityUser> signInManager, [FromBody] object? empty)
{
if (empty == null) return Unauthorized();
await signInManager.SignOutAsync();
return Ok();
}
} }

@ -10,13 +10,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup> </ItemGroup>

@ -1,6 +1,4 @@
using DbContextLib; using HeartTrackAPI;
using HeartTrackAPI.Utils;
using StubbedContextLib;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -18,8 +16,4 @@ var app = builder.Build();
init.Configure(app, app.Environment); init.Configure(app, app.Environment);
var context = app.Services.GetService<HeartTrackContext>() ?? app.Services.GetService<TrainingStubbedContext>();
context!.Database.EnsureCreated();
app.Run(); app.Run();

@ -7,6 +7,10 @@ public class PageRequest
{ {
public string? OrderingPropertyName { get; set; } = null; public string? OrderingPropertyName { get; set; } = null;
public bool? Descending { get; set; } = false; public bool? Descending { get; set; } = false;
// [Range(0, int.MaxValue, ErrorMessage = "Count must be greater than 0")]
public int Index { get; set; } = 0; public int Index { get; set; } = 0;
// [Range(0, int.MaxValue, ErrorMessage = "Count must be greater than 0")]
public int Count { get; set; } = 1; public int Count { get; set; } = 1;
} }

@ -1,49 +0,0 @@
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);
}
}

@ -8,10 +8,5 @@
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
"HeartTrackAuthConnection": "Data Source=uca_HeartTrack.db" "HeartTrackAuthConnection": "Data Source=uca_HeartTrack.db"
},
"JWT": {
"Issuer": "HeartTrack",
"Audience": "HeartTrack",
"SigningKey": "jythgfbhtgdfytrhdgfythrydfghtrydfythrjgfytjgfjkhljkloijijlijoukhjuhygyhdhcesxeqaewxsfcgevfbtgjnh61689346843njfdvcgdv"
} }
} }

@ -9,7 +9,6 @@ public static class EnumMappeur
return value switch return value switch
{ {
"None" => Shared.AthleteOrderCriteria.None, "None" => Shared.AthleteOrderCriteria.None,
"ById" => Shared.AthleteOrderCriteria.ById,
"ByUsername" => Shared.AthleteOrderCriteria.ByUsername, "ByUsername" => Shared.AthleteOrderCriteria.ByUsername,
"ByFirstName" => Shared.AthleteOrderCriteria.ByFirstName, "ByFirstName" => Shared.AthleteOrderCriteria.ByFirstName,
"ByLastName" => Shared.AthleteOrderCriteria.ByLastName, "ByLastName" => Shared.AthleteOrderCriteria.ByLastName,
@ -18,7 +17,7 @@ public static class EnumMappeur
"ByWeight" => Shared.AthleteOrderCriteria.ByWeight, "ByWeight" => Shared.AthleteOrderCriteria.ByWeight,
"ByDateOfBirth" => Shared.AthleteOrderCriteria.ByDateOfBirth, "ByDateOfBirth" => Shared.AthleteOrderCriteria.ByDateOfBirth,
"ByEmail" => Shared.AthleteOrderCriteria.ByEmail, "ByEmail" => Shared.AthleteOrderCriteria.ByEmail,
"ByRole" => Shared.AthleteOrderCriteria.ByRole, "ByIsCoach" => Shared.AthleteOrderCriteria.ByIsCoach,
_ => Shared.AthleteOrderCriteria.None _ => Shared.AthleteOrderCriteria.None
}; };
} }

@ -0,0 +1,9 @@
namespace Model.Manager;
public class ActivityManager : IActivityManager
{
public void AddActivityFromFitFile(byte filePath)
{
throw new NotImplementedException();
}
}

@ -0,0 +1,6 @@
namespace Model.Manager;
public interface IActivityManager
{
public void AddActivityFromFitFile(byte filePath);
}

@ -1,4 +1,3 @@
using Dto.Tiny;
using Model.Repository; using Model.Repository;
namespace Model.Manager; namespace Model.Manager;
@ -7,6 +6,4 @@ public interface IDataManager
{ {
IUserRepository UserRepo { get; } IUserRepository UserRepo { get; }
IActivityRepository ActivityRepo { get; } IActivityRepository ActivityRepo { get; }
IDataSourceRepository<DataSourceTinyDto> DataSourceRepo { get; }
} }

@ -0,0 +1,6 @@
namespace Model.Manager;
public class UserManager
{
}

@ -5,5 +5,5 @@ public class RelationshipRequest // : Observable
public int Id { get ; set ; } public int Id { get ; set ; }
public int FromUser { get ; set; } public int FromUser { get ; set; }
public int ToUser { get; set ; } public int ToUser { get; set ; }
public required string Status { get ; set; } public string Status { get ; }
} }

@ -1,18 +1,13 @@
using Dto;
using Dto.Tiny;
using Shared; using Shared;
namespace Model.Repository; namespace Model.Repository;
public interface IActivityRepository public interface IActivityRepository
{ {
public Task<IEnumerable<ActivityTinyDto>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false); public Task<IEnumerable<Activity>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false);
public Task<Activity?> GetActivityByIdAsync(int id); public Task<Activity?> GetActivityByIdAsync(int id);
public Task<ResponseActivityDto?> GetActivityById(int id);
public Task<Activity?> AddActivity(Activity activity); public Task<Activity?> AddActivity(Activity activity);
public Task<ResponseActivityDto?> AddActivity(NewActivityDto activity); public Task<Activity?> UpdateActivity(int id, Activity activity);
public Task<ResponseActivityDto?> UpdateActivity(int id, ActivityTinyDto activity);
public Task<bool> DeleteActivity(int id); public Task<bool> DeleteActivity(int id);
public Task<int> GetNbItems(); public Task<int> GetNbItems();
public Task<IEnumerable<Activity>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria orderCriteria, bool descending= false); public Task<IEnumerable<Activity>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria orderCriteria, bool descending= false);

@ -1,6 +0,0 @@
namespace Model.Repository;
public interface IDataSourceRepository<T>
{
Task<T?> GetItemById(int id);
}

@ -1,24 +1,17 @@
using Dto; using Shared;
using Dto.Tiny;
using Shared;
namespace Model.Repository; namespace Model.Repository;
public interface IUserRepository : IGenericRepository<User> // Make it generic public interface IUserRepository : IGenericRepository<User>
{ {
// [TODO] [Dave] DELETE it use just in the test
public Task<IEnumerable<User>?> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false); public Task<IEnumerable<User>?> GetUsers(int index, int count, AthleteOrderCriteria? criteria , bool descending = false);
public Task<IEnumerable<UserTinyDto>?> GetUsersTiny(int index, int count, AthleteOrderCriteria? criteria , bool descending = false);
public Task<bool> AddFollowing(int fromUser, int toUser);
public Task<bool> RemoveFollowing(int fromUser, int toUser);
// DELETE public Task<bool> AddFriend(User user, User friend);
public Task<IEnumerable<UserTinyDto>?> GetFriends(int user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false);
public Task<int> GetNbFriends(int user);
public Task<UserTinyDto?> UpdateUser(int old,UserTinyDto user);
public Task<ResponseUserDto> GetUserById(int id);
public Task<UserTinyDto?> GetUserTinyById(int id);
public Task<IEnumerable<User>?> GetAllAthletes(int index, int count, AthleteOrderCriteria? criteria, bool descending = false);
public Task<IEnumerable<User>?> GetAllCoaches(int index, int count, AthleteOrderCriteria? criteria, bool descending = false);
public Task<bool> RemoveFriend(User user, User friend);
// should be removed cause i just have to call the GetItem then get the friends
public Task<IEnumerable<User>?> GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false);
public Task<int> GetNbFriends(User user);
} }

@ -9,7 +9,7 @@ public class User
public string FirstName { get; set; } public string FirstName { get; set; }
public string Email { get; set; } public string Email { get; set; }
public string? MotDePasse { get; set; } public string? MotDePasse { get; set; }
public char Sexe { get; set; } public string Sexe { get; set; }
public float Lenght { get; set; } public float Lenght { get; set; }
public float Weight { get; set; } public float Weight { get; set; }
public DateTime DateOfBirth { get; set; } public DateTime DateOfBirth { get; set; }
@ -22,7 +22,7 @@ public class User
public List<User> Users { get; set; } = new List<User>(); public List<User> Users { get; set; } = new List<User>();
public List<DataSource> DataSources { get; set; } = new List<DataSource>(); public List<DataSource> DataSources { get; set; } = new List<DataSource>();
public User( string username, string profilePicture, string nom, string prenom, string email, string motDePasse, char sexe, float taille, float poids, DateTime dateNaissance, Role role) 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; Username = username;
ProfilePicture = profilePicture; ProfilePicture = profilePicture;

@ -1,63 +0,0 @@
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;
}
}

@ -1,278 +0,0 @@
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();
}
*/

@ -1,5 +1,3 @@
using Dto;
using Dto.Tiny;
using Model; using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
@ -7,7 +5,6 @@ using Model.Manager;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Entities; using Entities;
using EFMappers; using EFMappers;
using Entities2Dto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Model2Entities; namespace Model2Entities;
@ -17,20 +14,24 @@ public partial class DbDataManager : IDataManager
public class ActivityRepository : IActivityRepository public class ActivityRepository : IActivityRepository
{ {
private readonly DbDataManager _dataManager; private readonly DbDataManager _dataManager;
private readonly ILogger<DbDataManager> _logger; private readonly ILogger<ActivityRepository> _logger = new Logger<ActivityRepository>(new LoggerFactory());
public ActivityRepository(DbDataManager dbDataManager, ILogger<DbDataManager> logger)
// logger et tests sur tout et mapper
public ActivityRepository(DbDataManager dataManager)
{ {
this._dataManager = dbDataManager; _dataManager = dataManager;
this._logger = logger; // _logger = logger;
} }
public async Task<IEnumerable<ActivityTinyDto>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) public async Task<IEnumerable<Activity>> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false)
{ {
_logger.LogInformation($"GetActivities with index {index} and count {count}", index, count); _logger.LogInformation($"GetActivities with index {index} and count {count}", index, count);
_logger.LogInformation($"GetActivities with criteria {criteria} and descending {descending}", criteria, descending); _logger.LogInformation($"GetActivities with criteria {criteria} and descending {descending}", criteria, descending);
var activities = _dataManager.DbContext.ActivitiesSet var activities = _dataManager.DbContext.ActivitiesSet
.GetItemsWithFilterAndOrdering(b => true, index, count, criteria, descending).ToTinyDtos(); .Include(a => a.HeartRates)
.Include(a=>a.Athlete)
.Include(a=> a.DataSource).GetItemsWithFilterAndOrdering(b => true, index, count, criteria, descending).ToModels();
_logger.LogInformation($"Retrieved {activities.Count()} activities"); _logger.LogInformation($"Retrieved {activities.Count()} activities");
return await Task.FromResult(activities); return await Task.FromResult(activities);
@ -39,23 +40,8 @@ public partial class DbDataManager : IDataManager
public async Task<Activity?> GetActivityByIdAsync(int id) public async Task<Activity?> GetActivityByIdAsync(int id)
{ {
_logger.LogInformation($"GetActivityByIdAsync with id {id}", id); _logger.LogInformation($"GetActivityByIdAsync with id {id}", id);
// ! By None don't pass the filter
var activityEntity = await _dataManager.DbContext.ActivitiesSet.IncludeStandardProperties().SingleOrDefaultAsync(a => a.IdActivity == id); var activity = _dataManager.DbContext.ActivitiesSet.GetItemsWithFilterAndOrdering(b => b.IdActivity == id, 0, 1, ActivityOrderCriteria.None, false).First().ToModel();
var activity = activityEntity != null ? activityEntity.ToModel() : null;
if (activity != null)
_logger.LogInformation($"Retrieved activity with ID {id}");
else
_logger.LogWarning($"No activity found with ID {id}");
return await Task.FromResult(activity);
}
public async Task<ResponseActivityDto> GetActivityById(int id)
{
_logger.LogInformation($"GetActivityByIdAsync with id {id}", id);
var activityEntity = await _dataManager.DbContext.ActivitiesSet.IncludeAll(_dataManager.DbContext).SingleOrDefaultAsync(a => a.IdActivity == id);
var activity = activityEntity != null ? activityEntity.ToResponseDto() : null;
if (activity != null) if (activity != null)
_logger.LogInformation($"Retrieved activity with ID {id}"); _logger.LogInformation($"Retrieved activity with ID {id}");
@ -84,46 +70,18 @@ public partial class DbDataManager : IDataManager
} }
} }
public async Task<ResponseActivityDto?> AddActivity(NewActivityDto activity) public async Task<Activity?> UpdateActivity(int id, Activity activity)
{ {
try try
{ {
_logger.LogInformation("Adding new activity");
var addedActivity = activity.Activity.ToEntity();
addedActivity.DataSourceId = activity.DataSourceId;
addedActivity.AthleteId = activity.AthleteId;
addedActivity.HeartRates = activity.HeartRates.ToEntities().ToList();
await _dataManager.DbContext.AddItem(addedActivity);
_logger.LogInformation($"Added activity with ID {addedActivity.IdActivity}");
_dataManager.DbContext.SaveChanges();
return await Task.FromResult(addedActivity.ToResponseDto());
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while adding activity");
throw;
}
}
public async Task<ResponseActivityDto> UpdateActivity(int id, ActivityTinyDto activity)
{
try
{
var entity =await _dataManager.DbContext.ActivitiesSet.IncludeAll(_dataManager.DbContext)
.FirstOrDefaultAsync(a => a.IdActivity == id);
if (entity != null)
{
_logger.LogInformation($"Updating activity with ID {id}"); _logger.LogInformation($"Updating activity with ID {id}");
var updatedActivity = await _dataManager.DbContext.UpdateItem<Activity, ActivityEntity>(id, activity, (activity, entity) =>
{
entity.Type = activity.Type; entity.Type = activity.Type;
entity.Date = DateOnly.FromDateTime(activity.Date); entity.Date = DateOnly.FromDateTime(activity.Date);
entity.StartTime = TimeOnly.FromDateTime(activity.StartTime); entity.StartTime = TimeOnly.FromDateTime(activity.StartTime);
entity.EndTime = TimeOnly.FromDateTime(activity.EndTime); entity.EndTime = TimeOnly.FromDateTime(activity.EndTime);
entity.EffortFelt = activity.EffortFelt; entity.EffortFelt = activity.Effort;
entity.Variability = activity.Variability; entity.Variability = activity.Variability;
entity.Variance = activity.Variance; entity.Variance = activity.Variance;
entity.StandardDeviation = activity.StandardDeviation; entity.StandardDeviation = activity.StandardDeviation;
@ -132,15 +90,16 @@ public partial class DbDataManager : IDataManager
entity.Minimum = activity.Minimum; entity.Minimum = activity.Minimum;
entity.AverageTemperature = activity.AverageTemperature; entity.AverageTemperature = activity.AverageTemperature;
entity.HasAutoPause = activity.HasAutoPause; entity.HasAutoPause = activity.HasAutoPause;
_dataManager.DbContext.SaveChanges(); });
if (updatedActivity != null)
{
_logger.LogInformation($"Updated activity with ID {id}"); _logger.LogInformation($"Updated activity with ID {id}");
return await Task.FromResult(entity.ToResponseDto()); return await Task.FromResult(updatedActivity.ToModel());
} }
else else
{ {
_logger.LogError($"Failed to update activity with ID {id}"); _logger.LogError($"Failed to update activity with ID {id}");
return await Task.FromResult<ResponseActivityDto?>(null); return await Task.FromResult<Activity?>(null);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -187,33 +146,12 @@ public partial class DbDataManager : IDataManager
public Task<IEnumerable<Activity>> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false) public Task<IEnumerable<Activity>> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false)
{ {
try { throw new NotImplementedException();
_logger.LogInformation($"Getting activities for user with ID {userId}");
var activities = _dataManager.DbContext.ActivitiesSet
.IncludeStandardProperties().GetItemsWithFilterAndOrdering(b => b.AthleteId == userId, index, count, criteria, descending).ToModels();
_logger.LogInformation($"Retrieved {activities.Count()} activities for user with ID {userId}");
return Task.FromResult(activities);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred while getting activities for user with ID {userId}");
throw;
}
} }
public Task<int> GetNbActivitiesByUser(int userId) public Task<int> GetNbActivitiesByUser(int userId)
{ {
try { throw new NotImplementedException();
_logger.LogInformation($"Getting the total number of activities for user with ID {userId}");
var count = _dataManager.DbContext.ActivitiesSet.Count(b => b.AthleteId == userId);
_logger.LogInformation($"Total number of activities for user with ID {userId}: {count}");
return Task.FromResult(count);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred while getting the total number of activities for user with ID {userId}");
throw;
}
} }
} }
} }

@ -1,34 +0,0 @@
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();
}
}
}

@ -1,8 +1,6 @@
using DbContextLib; using DbContextLib;
using Dto.Tiny;
using EFMappers; using EFMappers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Model.Manager; using Model.Manager;
using Model.Repository; using Model.Repository;
@ -12,21 +10,15 @@ public partial class DbDataManager: IDataManager
{ {
public IActivityRepository ActivityRepo { get; } public IActivityRepository ActivityRepo { get; }
public IUserRepository UserRepo { get; } public IUserRepository UserRepo { get; }
public IDataSourceRepository<DataSourceTinyDto> DataSourceRepo { get; }
protected HeartTrackContext DbContext { get; } protected HeartTrackContext DbContext { get; }
protected readonly ILogger<DbDataManager> _logger = new Logger<DbDataManager>(new LoggerFactory());
// mettre si pb lors d'une requete si rollback ou pas // mettre si pb lors d'une requete si rollback ou pas
public DbDataManager(HeartTrackContext dbContext) public DbDataManager(HeartTrackContext dbContext)
{ {
DbContext = dbContext; DbContext = dbContext;
ActivityRepo = new ActivityRepository(this, _logger); ActivityRepo = new ActivityRepository(this);
UserRepo = new UserRepository(this, _logger); UserRepo = new UserRepository(this);
DataSourceRepo = new DataSourceRepository(this, _logger);
ActivityMapper.Reset(); ActivityMapper.Reset();
UserMappeur.Reset();
// Faire pour les autres reset() des autres mappers // Faire pour les autres reset() des autres mappers
} }
@ -37,8 +29,7 @@ public partial class DbDataManager: IDataManager
public DbDataManager() public DbDataManager()
{ {
DbContext = new HeartTrackContext(); DbContext = new HeartTrackContext();
ActivityRepo = new ActivityRepository(this, _logger); ActivityRepo = new ActivityRepository(this);
UserRepo= new UserRepository(this, _logger); UserRepo= new UserRepository(this);
DataSourceRepo = new DataSourceRepository(this, _logger);
} }
} }

@ -40,10 +40,13 @@ public static class Extensions
var existingT = await context.Set<U>().FindAsync(id); var existingT = await context.Set<U>().FindAsync(id);
if (existingT != null && newItem != null) if (existingT != null && newItem != null)
{ {
// Appliquer les mises à jour sur l'objet existant en utilisant l'action passée en paramètre
updateAction(newItem, existingT); updateAction(newItem, existingT);
// Marquer l'objet comme modifié dans le contexte
context.Update(existingT); context.Update(existingT);
// Enregistrer les modifications dans la base de données
await context.SaveChangesAsync(); await context.SaveChangesAsync();
return existingT; return existingT;
} }
@ -81,7 +84,7 @@ public static class Extensions
return descending ? list.OrderByDescending(x => propertyInfo.GetValue(x)) : list.OrderBy(x => propertyInfo.GetValue(x)); return descending ? list.OrderByDescending(x => propertyInfo.GetValue(x)) : list.OrderBy(x => propertyInfo.GetValue(x));
} }
public static IQueryable<TEntity> IncludeAll<TEntity>(this IQueryable<TEntity> query, HeartTrackContext dbContext) where TEntity : class public static IQueryable<TEntity> IncludeAll<TEntity>(this HeartTrackContext dbContext, IQueryable<TEntity> query) where TEntity : class
{ {
var entityType = dbContext.Model.FindEntityType(typeof(TEntity)); var entityType = dbContext.Model.FindEntityType(typeof(TEntity));
foreach (var navigation in entityType.GetNavigations()) foreach (var navigation in entityType.GetNavigations())
@ -90,18 +93,6 @@ public static class Extensions
} }
return query; return query;
} }
public static IQueryable<ActivityEntity> IncludeStandardProperties(this IQueryable<ActivityEntity> query)
{
return query.Include(a => a.HeartRates)
.Include(a => a.Athlete)
.Include(a => a.DataSource);
}
public static IQueryable<AthleteEntity> IncludeStandardProperties(this IQueryable<AthleteEntity> query)
{
return query.Include(a => a.DataSource)
.Include(a => a.Activities).ThenInclude(ac => ac.HeartRates).Include(ac => ac.Activities).ThenInclude(ac => ac.DataSource)
.Include(a => a.Image);
}
// public static Activity ToModel(this ActivityEntity entity) // public static Activity ToModel(this ActivityEntity entity)
// { // {
// return new Activity ( // return new Activity (

@ -8,10 +8,16 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\DbContextLib\DbContextLib.csproj" /> <ProjectReference Include="..\DbContextLib\DbContextLib.csproj" />
<ProjectReference Include="..\Entities2Dto\Entities2Dto.csproj" />
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\EFMappers\EFMappers.csproj" /> <ProjectReference Include="..\EFMappers\EFMappers.csproj" />
<ProjectReference Include="..\StubbedContextLib\StubbedContextLib.csproj" /> <ProjectReference Include="..\StubbedContextLib\StubbedContextLib.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project> </Project>

@ -1,13 +1,6 @@
using Dto;
using Dto.Tiny;
using Microsoft.Extensions.Logging;
using Model; using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
using EFMappers;
using Entities;
using Entities2Dto;
using Microsoft.EntityFrameworkCore;
namespace Model2Entities; namespace Model2Entities;
@ -16,323 +9,65 @@ public partial class DbDataManager
public class UserRepository : IUserRepository public class UserRepository : IUserRepository
{ {
private readonly DbDataManager _dataManager; private readonly DbDataManager _dataManager;
private readonly ILogger<DbDataManager> _logger;
public UserRepository(DbDataManager dbDataManager, ILogger<DbDataManager> logger) public UserRepository(DbDataManager dataManager)
{ {
_dataManager = dbDataManager; _dataManager = dataManager;
_logger = logger;
} }
public async Task<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null, public async Task<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null, bool descending = false)
bool descending = false)
=> await GetUsers(index, count, this.ToEnum(orderingProperty), descending);
public async Task<IEnumerable<User>> GetUsers(int index, int count,
AthleteOrderCriteria? orderingProperty = null, bool descending = false)
{
_logger.LogInformation($"GetItems with index {index} and count {count}", index, count);
_logger.LogInformation($"GetItems with orderingProperty {orderingProperty} and descending {descending}",
orderingProperty, descending);
var users = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().GetItemsWithFilterAndOrdering(b => true, index, count,
orderingProperty != AthleteOrderCriteria.None ? orderingProperty : null, descending).ToModels();
_logger.LogInformation($"Retrieved {users.Count()} users");
return await Task.FromResult(users);
}
public async Task<IEnumerable<UserTinyDto>?> GetUsersTiny(int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{ {
throw new NotImplementedException();
var users = _dataManager.DbContext.AthletesSet.GetItemsWithFilterAndOrdering(b => true,
index, count,
criteria != AthleteOrderCriteria.None ? criteria : null, descending);
_logger.LogInformation($"Retrieved {users.Count()} users");
return await Task.FromResult(users.ToTinyDtos());
} }
public async Task<User?> GetItemById(int id) public async Task<User?> GetItemById(int id)
{ {
throw new NotImplementedException();
_logger.LogInformation($"GetItemById with id {id}", id);
var userEntity = await _dataManager.DbContext.AthletesSet.IncludeStandardProperties()
.SingleOrDefaultAsync(a => a.Id == id);
var user = userEntity != null ? userEntity.ToModel() : null;
if (user != null)
_logger.LogInformation($"Retrieved user with ID {id}");
else
_logger.LogWarning($"No user found with ID {id}");
return await Task.FromResult(user);
}
public async Task<UserTinyDto?> UpdateUser(int old, UserTinyDto user)
{
_logger.LogInformation($"UpdateUser with id {old}", old);
var originalEntity = _dataManager.DbContext.AthletesSet.Find(old);
if (originalEntity == null)
{
_logger.LogWarning($"No user found with ID {old}");
return await Task.FromResult<UserTinyDto?>(null);
}
var originalEntry = _dataManager.DbContext.Entry(originalEntity);
var values = typeof(AthleteEntity).GetProperties().Where(ppty => ppty.Name != "IdAthlete")
.ToDictionary(ppty => ppty.Name, ppty => ppty.GetValue(user.ToEntity()));
originalEntry.CurrentValues.SetValues(values);
_dataManager.DbContext.AthletesSet.Attach(originalEntity);
_dataManager.DbContext.Entry(originalEntity).State = EntityState.Modified;
_dataManager.DbContext.SaveChanges();
var updatedUser = originalEntity.ToTinyDto();
if (updatedUser != null)
_logger.LogInformation($"Updated user with ID {old}");
else
_logger.LogWarning($"No user found with ID {old}");
return await Task.FromResult(updatedUser);
}
public async Task<ResponseUserDto> GetUserById(int id)
{
_logger.LogInformation($"GetTinyItemById with id {id}", id);
var userEntity = await _dataManager.DbContext.AthletesSet.IncludeStandardProperties().Include(a => a.Followers).Include(a => a.Followings)
.SingleOrDefaultAsync(a => a.Id == id);
var user = userEntity != null ? userEntity.ToResponseDto() : null;
if (user != null)
_logger.LogInformation($"Retrieved user with ID {id}");
else
_logger.LogWarning($"No user found with ID {id}");
return user;
}
public Task<UserTinyDto?> GetUserTinyById(int id)
{
_logger.LogInformation($"GetTinyItemById with id {id}", id);
var userEntity = _dataManager.DbContext.AthletesSet.FindAsync(id).Result;
var user = userEntity != null ? userEntity.ToTinyDto() : null;
if (user != null)
_logger.LogInformation($"Retrieved user with ID {id}");
else
_logger.LogWarning($"No user found with ID {id}");
return Task.FromResult(user);
} }
public async Task<User?> UpdateItem(int oldItem, User newItem) public async Task<User?> UpdateItem(int oldItem, User newItem)
{ {
_logger.LogInformation($"UpdateItem with id {oldItem}", oldItem); throw new NotImplementedException();
var originalEntity = _dataManager.DbContext.AthletesSet.Find(oldItem);
if (originalEntity == null)
{
_logger.LogWarning($"No user found with ID {oldItem}");
return await Task.FromResult<User?>(null);
}
var originalEntry = _dataManager.DbContext.Entry(originalEntity);
var values = typeof(AthleteEntity).GetProperties().Where(ppty => ppty.Name != "IdAthlete")
.ToDictionary(ppty => ppty.Name, ppty => ppty.GetValue(newItem.ToEntity()));
originalEntry.CurrentValues.SetValues(values);
_dataManager.DbContext.AthletesSet.Attach(originalEntity);
_dataManager.DbContext.Entry(originalEntity).State = EntityState.Modified;
_dataManager.DbContext.SaveChanges();
var updatedUser = originalEntity.ToModel();
if (updatedUser != null)
_logger.LogInformation($"Updated user with ID {oldItem}");
else
_logger.LogWarning($"No user found with ID {oldItem}");
return await Task.FromResult(updatedUser);
} }
public async Task<User?> AddItem(User item) public async Task<User?> AddItem(User item)
{ {
throw new NotImplementedException();
_logger.LogInformation("Adding new user");
var addedUser = (await _dataManager.DbContext.AddItem(item.ToEntity()))?.ToModel();
if (addedUser != null)
_logger.LogInformation($"Added user with ID {addedUser.Id}");
else
_logger.LogError("Failed to add user");
return await Task.FromResult(addedUser);
} }
public async Task<bool> DeleteItem(int item) public async Task<bool> DeleteItem(int item)
{ {
throw new NotImplementedException();
_logger.LogInformation($"DeleteItem with id {item}", item);
var deleted = await _dataManager.DbContext.DeleteItem<AthleteEntity>(item);
if (deleted)
_logger.LogInformation($"Deleted user with ID {item}");
else
_logger.LogWarning($"No user found with ID {item}");
return await Task.FromResult(deleted);
} }
public async Task<int> GetNbItems() public async Task<int> GetNbItems()
{ {
_logger.LogInformation("GetNbItems"); throw new NotImplementedException();
var nbItems = await _dataManager.DbContext.AthletesSet.CountAsync();
_logger.LogInformation($"Retrieved {nbItems} users");
return await Task.FromResult(nbItems);
}
public async Task<IEnumerable<User>> GetAllAthletes(int index, int count, AthleteOrderCriteria? criteria,
bool descending = false)
{
_logger.LogInformation($"GetAllAthletes with index {index} and count {count}", index, count);
_logger.LogInformation($"GetAllAthletes with criteria {criteria} and descending {descending}", criteria,
descending);
var athletes = _dataManager.DbContext.AthletesSet.IncludeStandardProperties()
.GetItemsWithFilterAndOrdering(a => a.IsCoach == false, index, count,
criteria != AthleteOrderCriteria.None ? criteria : null, descending).ToModels();
_logger.LogInformation($"Retrieved {athletes.Count()} athletes");
return await Task.FromResult(athletes);
}
public async Task<IEnumerable<User>> GetAllCoaches(int index, int count, AthleteOrderCriteria? criteria,
bool descending = false)
{
_logger.LogInformation($"GetAllCoaches with index {index} and count {count}", index, count);
_logger.LogInformation($"GetAllCoaches with criteria {criteria} and descending {descending}", criteria,
descending);
var coaches = _dataManager.DbContext.AthletesSet.IncludeStandardProperties()
.GetItemsWithFilterAndOrdering(a => a.IsCoach, index, count,
criteria != AthleteOrderCriteria.None ? criteria : null, descending).ToModels();
_logger.LogInformation($"Retrieved {coaches.Count()} coaches");
return await Task.FromResult(coaches);
}
public async Task<bool> AddFollowing(int fromUser, int toUser)
{
_logger.LogInformation($"Attempting to add following: User {fromUser} adding Following {toUser}");
var userEntity = _dataManager.DbContext.AthletesSet
.Include(a => a.Followings)
.FirstOrDefault(a => a.Id == fromUser);
if (userEntity == null)
{
_logger.LogWarning($"User not found: User {fromUser}");
throw new FriendShipException("User with id " + fromUser + " not found");
} }
if (userEntity.Followings.Any(f => f.FollowingId == toUser)) public async Task<IEnumerable<User>> GetUsers(int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{ {
_logger.LogInformation($"Following already exists: User {fromUser} and Following {toUser}"); throw new NotImplementedException();
throw new FriendShipException("Following already exists");
}
await _dataManager.DbContext.SaveChangesAsync();
userEntity.Followings.Add(new FriendshipEntity
{
FollowingId = toUser,
FollowerId = fromUser,
StartDate = DateTime.Now
});
await _dataManager.DbContext.SaveChangesAsync();
_logger.LogInformation($"Successfully following: from User {fromUser} to Following {toUser}");
return true;
}
public async Task<bool> RemoveFollowing(int fromUser, int toUser){
_logger.LogInformation($"Attempting to remove following: User {fromUser} removing Following {toUser}");
var userEntity = _dataManager.DbContext.AthletesSet
.Include(a => a.Followings)
.FirstOrDefault(a => a.Id == fromUser);
if (userEntity == null)
{
_logger.LogWarning($"User not found: User {fromUser}");
throw new FriendShipException("User with id " + fromUser + " not found");
}
var friendship = userEntity.Followings.FirstOrDefault(f => f.FollowingId == toUser);
if (friendship == null)
{
_logger.LogInformation($"Following not found: User {fromUser} and Following {toUser}");
throw new FriendShipException("Following not found");
}
await _dataManager.DbContext.SaveChangesAsync();
userEntity.Followings.Remove(friendship);
await _dataManager.DbContext.SaveChangesAsync();
_logger.LogInformation($"Successfully removed following: from User {fromUser} to Following {toUser}");
return await Task.FromResult(true);
} }
public async Task<IEnumerable<UserTinyDto>?> GetFriends(int userId, int index, int count, AthleteOrderCriteria? criteria,
bool descending = false)
{
try
{
_logger.LogInformation($"GetFriends called with index {index}, count {count}, criteria {criteria}, and descending {descending}");
var athlete = await _dataManager.DbContext.AthletesSet public async Task<bool> AddFriend(User user, User friend)
.Include(a => a.Followers).ThenInclude(f => f.Follower)
.Include(a => a.Followings).ThenInclude(f => f.Following)
.FirstOrDefaultAsync(a => a.Id == userId); // Use async version for better performance
if (athlete == null)
{ {
_logger.LogError("Athlete with id {id} not found", userId); throw new NotImplementedException();
throw new ModelNotFoundException($"Athlete with id {userId} not found");
} }
var friendsDtos = athlete.Followings public async Task<bool> RemoveFriend(User user, User friend)
.Where(f => athlete.Followers.Any(ff => ff.FollowerId == f.FollowingId))
.Select(f => f.Following).GetItemsWithFilterAndOrdering(a => true, index, count,
criteria != AthleteOrderCriteria.None ? criteria : null, descending).ToTinyDtos();
var userTinyDtos = friendsDtos.ToArray();
_logger.LogInformation($"Retrieved {userTinyDtos.Count()} friends for user {userId}");
return userTinyDtos;
}
catch (Exception ex)
{ {
_logger.LogError(ex.Message, ex.InnerException, ex.StackTrace); throw new NotImplementedException();
return null;
}
} }
public async Task<int> GetNbFriends(int userId) public Task<IEnumerable<User>?>? GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{ {
try throw new NotImplementedException();
{
_logger.LogInformation($"GetNbFriends called for user {userId}");
var athlete = await _dataManager.DbContext.AthletesSet
.Include(a => a.Followers).ThenInclude(f => f.Follower)
.Include(a => a.Followings).ThenInclude(f => f.Following)
.FirstOrDefaultAsync(a => a.Id == userId);
if (athlete == null)
{
_logger.LogError("Athlete with id {id} not found", userId);
throw new ModelNotFoundException($"Athlete with id {userId} not found");
} }
// Count the number of mutual friendships public Task<int> GetNbFriends(User user)
var nbFriends = athlete.Followings
.Count(f => athlete.Followers.Any(ff => ff.FollowerId == f.FollowingId));
_logger.LogInformation($"User {userId} has {nbFriends} friends");
return nbFriends;
}
catch (Exception ex)
{ {
_logger.LogError(ex, "An error occurred while counting friends for user {UserId}", userId); throw new NotImplementedException();
throw; // Consider handling the exception outside of this method or logging it accordingly.
} }
} }
}
} }

@ -3,20 +3,13 @@ namespace Shared;
public enum ActivityOrderCriteria public enum ActivityOrderCriteria
{ {
None, None,
ByIdActivity,
ByType, ByType,
ByDate, ByDate,
ByStartTime, ByDuration,
ByEndTime, ByDistance,
ByEffortFelt, ByElevation,
ByVariability, ByAverageSpeed,
ByVariance, ByAverageHeartRate,
ByStandardDeviation, ByCalories,
ByAverage, ByDescription
ByMaximum,
ByMinimum,
ByAverageTemperature,
ByHasAutoPause,
ByDataSourceId,
ByAthleteId
} }

@ -3,16 +3,43 @@
public enum AthleteOrderCriteria public enum AthleteOrderCriteria
{ {
None, None,
ById,
ByUsername, ByUsername,
ByFirstName, ByFirstName,
ByLastName, ByLastName,
ByEmail,
BySexe, BySexe,
ByLenght, ByLenght,
ByWeight, ByWeight,
ByDateOfBirth, ByDateOfBirth,
ByRole 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;
} }
}*/

@ -2,25 +2,25 @@ namespace Shared;
public static class Extensions public static class Extensions
{ {
public static U ToU<T, U>(this T t, GenericMapper<T, U> mapper, Func<T, U> func,Action<T, U>? action = null,bool useMapper = true) where U :class where T :class public static U ToU<T, U>(this T t, GenericMapper<T, U> mapper, Func<T, U> func,Action<T, U>? action = null) where U :class where T :class
{ {
var res = mapper.GetU(t); var res = mapper.GetU(t);
if (res != null) return res; if (res != null) return res;
U u = func(t); U u = func(t);
if(useMapper) mapper.Add(t, u); mapper.Add(t, u);
if(action != null) action(t, u); if(action != null) action(t, u);
return u; return u;
} }
// , Action<T, U> action // , Action<T, U> action
public static T ToT<T,U>(this U u, GenericMapper<T, U> mapper, Func<U, T> func,Action<U, T>? action = null,bool useMapper = true) where U :class where T :class public static T ToT<T,U>(this U u, GenericMapper<T, U> mapper, Func<U, T> func,Action<U, T>? action = null) where U :class where T :class
{ {
var result = mapper.GetT(u); var result = mapper.GetT(u);
if(result != null) return result; if(result != null) return result;
T t = func(u); T t = func(u);
if(useMapper) mapper.Add(t, u); mapper.Add(t, u);
if(action != null) action(u, t); if(action != null) action(u, t);
return t; return t;

@ -1,15 +0,0 @@
namespace Shared;
public class FriendShipException : ModelNotFoundException
{
public FriendShipException(string message) : base(message)
{
}
}
public class ModelNotFoundException : Exception
{
public ModelNotFoundException(string message) : base(message)
{
}
}

@ -1,5 +1,3 @@
using Dto;
using Dto.Tiny;
using Model; using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
@ -8,17 +6,17 @@ namespace StubAPI;
public class ActivityService: IActivityRepository public class ActivityService: IActivityRepository
{ {
private List<ActivityTinyDto> _activities = new List<ActivityTinyDto>( private List<Activity> _activities = new List<Activity>(
new ActivityTinyDto[] new Activity[]
{ {
new ActivityTinyDto new Activity
{ {
Id = 1, Id = 1,
Type = "Running", Type = "Running",
Date = new DateTime(2021, 10, 10), Date = new DateTime(2021, 10, 10),
StartTime = new DateTime(2021, 10, 10, 10, 0, 0), StartTime = new DateTime(2021, 10, 10, 10, 0, 0),
EndTime = new DateTime(2021, 10, 10, 11, 0, 0), EndTime = new DateTime(2021, 10, 10, 11, 0, 0),
EffortFelt = 3, Effort = 3,
Variability = 0.5f, Variability = 0.5f,
Variance = 0.5f, Variance = 0.5f,
StandardDeviation = 0.5f, StandardDeviation = 0.5f,
@ -27,46 +25,33 @@ public class ActivityService: IActivityRepository
Minimum = 0, Minimum = 0,
AverageTemperature = 20.0f, AverageTemperature = 20.0f,
HasAutoPause = false, HasAutoPause = false,
Athlete = 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<IEnumerable<ActivityTinyDto>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) public async Task<IEnumerable<Activity>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false)
=> await Task.FromResult(_activities.GetItemsWithFilterAndOrdering(a => true, index, count, criteria, descending)); => await Task.FromResult(_activities.GetItemsWithFilterAndOrdering(c=>true,index, count,criteria != ActivityOrderCriteria.None ? criteria: null , descending));
public Task<Activity?> GetActivityByIdAsync(int id) public Task<Activity?> GetActivityByIdAsync(int id)
{ {
throw new NotImplementedException(); return Task.FromResult(_activities.FirstOrDefault(s => s.Id == id));
// return Task.FromResult(_activities.FirstOrDefault(s => s.Id == id)?.ToModel());
}
public async Task<ResponseActivityDto?> GetActivityById(int id)
{
throw new NotImplementedException();
} }
public Task<Activity?> AddActivity(Activity activity) public Task<Activity?> AddActivity(Activity activity)
=> throw new NotImplementedException(); => _activities.AddItem(activity);
public async Task<ResponseActivityDto?> AddActivity(NewActivityDto activity)
{
throw new NotImplementedException();
}
public async Task<ResponseActivityDto?> UpdateActivity(int id, ActivityTinyDto activity)
{
throw new NotImplementedException();
}
public async Task<Activity?> UpdateActivity(int id, Activity activity) public async Task<Activity?> UpdateActivity(int id, Activity activity)
{ {
throw new NotImplementedException(); var oldActivity = _activities.FirstOrDefault(s => s.Id == id);
/*var oldActivity = _activities.FirstOrDefault(s => s.Id == id);
if (oldActivity == null) return null; if (oldActivity == null) return null;
return await _activities.UpdateItem(oldActivity, activity);*/ return await _activities.UpdateItem(oldActivity, activity);
} }
public Task<bool> DeleteActivity(int id) public Task<bool> DeleteActivity(int id)
@ -81,16 +66,13 @@ public class ActivityService: IActivityRepository
public async Task<IEnumerable<Activity>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false) public async Task<IEnumerable<Activity>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria criteria, bool descending = false)
{ {
throw new NotImplementedException(); var activities = _activities.GetItemsWithFilterAndOrdering(a => a.Athlete.Id == userId, index, count,
/* var activities = _activities.GetItemsWithFilterAndOrdering(a => a.Athlete.Id == userId, index, count,
criteria != ActivityOrderCriteria.None ? criteria : null, descending); criteria != ActivityOrderCriteria.None ? criteria : null, descending);
return await Task.FromResult(activities);*/ return await Task.FromResult(activities);
} }
public Task<int> GetNbActivitiesByUser(int userId) public Task<int> GetNbActivitiesByUser(int userId)
{ {
throw new NotImplementedException(); return Task.FromResult(_activities.Count(a => a.Athlete.Id == userId));
// return Task.FromResult(_activities.Count(a => a.Athlete.Id == userId));
} }
} }

@ -1,6 +1,4 @@
using Dto; using Model;
using Dto.Tiny;
using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
@ -13,21 +11,21 @@ public class UserService : IUserRepository
new User 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", 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), Sexe = "M", Lenght = 180, Weight = 70, DateOfBirth = new DateTime(1990, 1, 1),
Email = "john.doe@example.com", Role = new Athlete() Email = "john.doe@example.com", Role = new Athlete()
}, },
new User 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", 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), Sexe = "F", Lenght = 170, Weight = 60, DateOfBirth = new DateTime(1992, 2, 2),
Email = "athlete2@example.com", Role = new Coach() Email = "athlete2@example.com", Role = new Coach()
}, },
new User 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", 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", Sexe = "M", Lenght = 190, Weight = 80, DateOfBirth = new DateTime(1994, 3, 3), Email = "ath@ex.fr",
Role = new Athlete() Role = new Athlete()
} }
@ -36,26 +34,6 @@ public class UserService : IUserRepository
public async Task<IEnumerable<User>> GetUsers(int index, int count, AthleteOrderCriteria? orderingProperty = null, bool descending = false) public async Task<IEnumerable<User>> GetUsers(int index, int count, AthleteOrderCriteria? orderingProperty = null, bool descending = false)
=> athletes.GetItemsWithFilterAndOrdering(c=>true,index, count,orderingProperty != AthleteOrderCriteria.None ? orderingProperty: null , descending); => athletes.GetItemsWithFilterAndOrdering(c=>true,index, count,orderingProperty != AthleteOrderCriteria.None ? orderingProperty: null , descending);
public async Task<IEnumerable<UserTinyDto>?> GetUsersTiny(int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{
throw new NotImplementedException();
}
public async Task<bool> AddFollowing(int fromUser, int toUser)
{
throw new NotImplementedException();
}
public async Task<bool> RemoveFollowing(int fromUser, int toUser)
{
throw new NotImplementedException();
}
public async Task<IEnumerable<UserTinyDto>?> GetFriends(int user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{
throw new NotImplementedException();
}
public async Task<bool> AddFriend(User user, User friend) public async Task<bool> AddFriend(User user, User friend)
{ {
if (user == null || friend == null) if (user == null || friend == null)
@ -93,24 +71,9 @@ public class UserService : IUserRepository
public async Task<IEnumerable<User>?>? GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false) public async Task<IEnumerable<User>?>? GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
=>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.GetItemsWithFilterAndOrdering(c=>true,index, count,criteria, descending)); =>await Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.GetItemsWithFilterAndOrdering(c=>true,index, count,criteria, descending));
public Task<int> GetNbFriends(int user) public Task<int> GetNbFriends(User user)
{ {
return Task.FromResult(athletes.FirstOrDefault(s => s.Id == user)?.Users.Count ?? 0); return Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.Count ?? 0);
}
public async Task<UserTinyDto?> UpdateUser(int old, UserTinyDto user)
{
throw new NotImplementedException();
}
public async Task<ResponseUserDto> GetUserById(int id)
{
throw new NotImplementedException();
}
public Task<UserTinyDto?> GetUserTinyById(int id)
{
throw new NotImplementedException();
} }
public async Task<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null, public async Task<IEnumerable<User>> GetItems(int index, int count, string? orderingProperty = null,
@ -145,14 +108,4 @@ public class UserService : IUserRepository
{ {
return await Task.FromResult(athletes.Count); return await Task.FromResult(athletes.Count);
} }
public async Task<IEnumerable<User>> GetAllAthletes(int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{
return await Task.FromResult(athletes.GetItemsWithFilterAndOrdering(c=>c.Role is Athlete,index, count,criteria, descending));
}
public async Task<IEnumerable<User>> GetAllCoaches(int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{
return await Task.FromResult(athletes.GetItemsWithFilterAndOrdering(c=>c.Role is Coach,index, count,criteria, descending));
}
} }

@ -7,7 +7,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Entities2Dto\Entities2Dto.csproj" />
<ProjectReference Include="..\Model\Model.csproj" /> <ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup> </ItemGroup>

@ -1,4 +1,3 @@
using Dto.Tiny;
using Model.Manager; using Model.Manager;
using Model.Repository; using Model.Repository;
@ -8,7 +7,6 @@ public class StubData : IDataManager
{ {
public IUserRepository UserRepo { get; } public IUserRepository UserRepo { get; }
public IActivityRepository ActivityRepo { get; } public IActivityRepository ActivityRepo { get; }
public IDataSourceRepository<DataSourceTinyDto> DataSourceRepo { get; }
public StubData() public StubData()
{ {

@ -0,0 +1,4 @@
// using Shared;
// namespace

@ -42,11 +42,11 @@ namespace StubbedContextLib
modelBuilder.Entity<LargeImageEntity>().HasData(picture); modelBuilder.Entity<LargeImageEntity>().HasData(picture);
modelBuilder.Entity<AthleteEntity>().HasData( modelBuilder.Entity<AthleteEntity>().HasData(
new AthleteEntity { Id = 1, UserName = "Doe",ProfilPicture = picture2, ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}") ,LastName = "Doe", FirstName = "John", Email = "john.doe@example.com", PasswordHash = "password123", Sexe = 'M', Length = 1.80, Weight = 75, DateOfBirth = new DateOnly(1990, 01, 01), IsCoach = true , DataSourceId = 1}, new AthleteEntity { IdAthlete = 1, Username = "Doe",ProfilPicture = picture2, ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}") ,LastName = "Doe", FirstName = "John", Email = "john.doe@example.com", Password = "password123", Sexe = "M", Length = 1.80, Weight = 75, DateOfBirth = new DateOnly(1990, 01, 01), IsCoach = true , DataSourceId = 1},
new AthleteEntity { Id = 2, UserName = "Smith",ProfilPicture = picture2,LastName = "Smith", FirstName = "Jane", Email = "jane.smith@exemple.com", PasswordHash = "secure456", Sexe = 'F', Length = 1.65, Weight = 60, DateOfBirth = new DateOnly(1995, 01, 01), IsCoach = false, DataSourceId = 1 }, new AthleteEntity { IdAthlete = 2, Username = "Smith",ProfilPicture = picture2,ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}") ,LastName = "Smith", FirstName = "Jane", Email = "jane.smith@exemple.com", Password = "secure456", Sexe = "F", Length = 1.65, Weight = 60, DateOfBirth = new DateOnly(1995, 01, 01), IsCoach = false, DataSourceId = 1 },
new AthleteEntity { Id = 3, UserName = "Martin",ProfilPicture = picture2,ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}") ,LastName = "Martin", FirstName = "Paul", Email = "paul.martin@example.com", PasswordHash = "super789", Sexe = 'M', Length = 1.75, Weight = 68, DateOfBirth = new DateOnly(1992, 01, 01), IsCoach = true, DataSourceId = 1}, new AthleteEntity { IdAthlete = 3, Username = "Martin",ProfilPicture = picture2,ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}") ,LastName = "Martin", FirstName = "Paul", Email = "paul.martin@example.com", Password = "super789", Sexe = "M", Length = 1.75, Weight = 68, DateOfBirth = new DateOnly(1992, 01, 01), IsCoach = true, DataSourceId = 1},
new AthleteEntity { Id = 4, UserName = "Brown",ProfilPicture = picture2, ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}"),LastName = "Brown", FirstName = "Anna", Email = "anna.brown@example.com", PasswordHash = "test000", Sexe = 'F', Length = 1.70, Weight = 58, DateOfBirth = new DateOnly(1993, 01, 01), IsCoach = false, DataSourceId = 2 }, new AthleteEntity { IdAthlete = 4, Username = "Brown",ProfilPicture = picture2, ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}"),LastName = "Brown", FirstName = "Anna", Email = "anna.brown@example.com", Password = "test000", Sexe = "F", Length = 1.70, Weight = 58, DateOfBirth = new DateOnly(1993, 01, 01), IsCoach = false, DataSourceId = 2 },
new AthleteEntity { Id = 5, UserName = "Lee", ProfilPicture = picture2, ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}"),LastName = "Lee", FirstName = "Bruce", Email = "bruce.lee@example.com", PasswordHash = "hello321", Sexe = 'M', Length = 2.0, Weight = 90, DateOfBirth = new DateOnly(1991, 01, 01), IsCoach = false, DataSourceId = 3 } new AthleteEntity { IdAthlete = 5, Username = "Lee", ProfilPicture = picture2, ImageId = Guid.Parse("{8d121cdc-6787-4738-8edd-9e026ac16b65}"),LastName = "Lee", FirstName = "Bruce", Email = "bruce.lee@example.com", Password = "hello321", Sexe = "M", Length = 2.0, Weight = 90, DateOfBirth = new DateOnly(1991, 01, 01), IsCoach = false, DataSourceId = 3 }
); );
} }
} }

@ -39,7 +39,10 @@ namespace StubbedContextLib
modelBuilder.Entity<FriendshipEntity>().HasData( modelBuilder.Entity<FriendshipEntity>().HasData(
new FriendshipEntity { FollowerId = 1, FollowingId = 2 }, new FriendshipEntity { FollowerId = 1, FollowingId = 2 },
new FriendshipEntity { FollowerId = 1, FollowingId = 3 }, new FriendshipEntity { FollowerId = 1, FollowingId = 3 },
new FriendshipEntity { FollowerId = 3, FollowingId = 1 } new FriendshipEntity { FollowerId = 1, FollowingId = 4 },
new FriendshipEntity { FollowerId = 1, FollowingId = 5 },
new FriendshipEntity { FollowerId = 2, FollowingId = 1 },
new FriendshipEntity { FollowerId = 2, FollowingId = 3 }
); );
} }
} }

@ -8,22 +8,16 @@ using Shared;
using StubbedContextLib; using StubbedContextLib;
using static Model2Entities.DbDataManager; using static Model2Entities.DbDataManager;
namespace ConsoleTestEFMapper; namespace Model2Entities
{
class Program
static class Program
{ {
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
// Instanciation de DbDataManager et ActivityRepository // Instanciation de DbDataManager et ActivityRepository
Console.WriteLine(""); var dataManager = new DbDataManager(new TrainingStubbedContext());
var logger = new Logger<ActivityRepository>(new LoggerFactory());
} var activityRepository = new ActivityRepository(dataManager);
/*
static async Task ActivitiesTestAsync(DbDataManager dataManager, ILogger<DbDataManager> logger)
{
var activityRepository = new ActivityRepository(dataManager, logger);
// Test de la méthode GetActivities // Test de la méthode GetActivities
Console.WriteLine("Testing GetActivities method..."); Console.WriteLine("Testing GetActivities method...");
@ -47,7 +41,7 @@ namespace ConsoleTestEFMapper;
} }
Console.WriteLine(); Console.WriteLine();
// Test de la méthode AddActivity // // Test de la méthode AddActivity
Console.WriteLine("Testing AddActivity method..."); Console.WriteLine("Testing AddActivity method...");
var user = new User var user = new User
{ {
@ -82,18 +76,18 @@ namespace ConsoleTestEFMapper;
} }
Console.WriteLine(); Console.WriteLine();
// Test de la méthode UpdateActivity // // Test de la méthode UpdateActivity
Console.WriteLine("Testing UpdateActivity method..."); // Console.WriteLine("Testing UpdateActivity method...");
var updatedActivity = await activityRepository.UpdateActivity(6, new Activity { Id = 6, Type = "Updated Activity", Date = new DateTime(2021, 10, 11), StartTime = new DateTime(10, 10, 10, 10, 10, 10), EndTime = new DateTime(10, 10, 10, 12, 12, 12) }); // var updatedActivity = await activityRepository.UpdateActivity(6, new Activity { Id = 6, Name = "Updated Activity" });
if (updatedActivity != null) // if (updatedActivity != null)
{ // {
Console.WriteLine($"Activity updated successfully: ID: {updatedActivity.Id}, Name: {updatedActivity.Type}, Date: {updatedActivity.Date}, Start Time: {updatedActivity.StartTime}, End Time: {updatedActivity.EndTime}"); // Console.WriteLine($"Activity updated successfully: ID: {updatedActivity.Id}, Name: {updatedActivity.Name}");
} // }
else // else
{ // {
Console.WriteLine("Failed to update activity."); // Console.WriteLine("Failed to update activity.");
} // }
Console.WriteLine(); // Console.WriteLine();
// Test de la méthode DeleteActivity // Test de la méthode DeleteActivity
Console.WriteLine("Testing DeleteActivity method..."); Console.WriteLine("Testing DeleteActivity method...");
@ -111,6 +105,5 @@ namespace ConsoleTestEFMapper;
var itemCount = await activityRepository.GetNbItems(); var itemCount = await activityRepository.GetNbItems();
Console.WriteLine($"Total number of activities: {itemCount}"); Console.WriteLine($"Total number of activities: {itemCount}");
} }
static void UsersTest() }
{}*/
} }

@ -56,34 +56,34 @@ class Program
foreach (var athlete in db.AthletesSet) foreach (var athlete in db.AthletesSet)
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
Console.WriteLine("Accès à l'athlete d'id '2' :"); Console.WriteLine("Accès à l'athlete d'id '2' :");
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.Id == 2)) foreach (var athlete in db.AthletesSet.Where(a => a.IdAthlete == 2))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
Console.WriteLine("Accès à l'athlete de username 'Doe' :"); Console.WriteLine("Accès à l'athlete de username 'Doe' :");
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.UserName == "Doe")) foreach (var athlete in db.AthletesSet.Where(a => a.Username == "Doe"))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
Console.WriteLine("Accès à l'athlete de sexe 'F' :"); Console.WriteLine("Accès à l'athlete de sexe 'F' :");
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.Sexe == 'F')) foreach (var athlete in db.AthletesSet.Where(a => a.Sexe == "F"))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -92,7 +92,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.Email == "bruce.lee@example.com")) foreach (var athlete in db.AthletesSet.Where(a => a.Email == "bruce.lee@example.com"))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -101,7 +101,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.Weight == 90)) foreach (var athlete in db.AthletesSet.Where(a => a.Weight == 90))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -110,7 +110,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.Length == 1.80)) foreach (var athlete in db.AthletesSet.Where(a => a.Length == 1.80))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -119,7 +119,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.DateOfBirth == new DateOnly(1990, 01, 01))) foreach (var athlete in db.AthletesSet.Where(a => a.DateOfBirth == new DateOnly(1990, 01, 01)))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -128,7 +128,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.LastName == "Martin")) foreach (var athlete in db.AthletesSet.Where(a => a.LastName == "Martin"))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -137,7 +137,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.FirstName == "Anna")) foreach (var athlete in db.AthletesSet.Where(a => a.FirstName == "Anna"))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -146,7 +146,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.LastName == "Brown" && a.FirstName == "Anna")) foreach (var athlete in db.AthletesSet.Where(a => a.LastName == "Brown" && a.FirstName == "Anna"))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -155,7 +155,7 @@ class Program
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.IsCoach == true)) foreach (var athlete in db.AthletesSet.Where(a => a.IsCoach == true))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
Console.WriteLine("---------------------------------\n"); Console.WriteLine("---------------------------------\n");
@ -755,7 +755,7 @@ class Program
Console.WriteLine("Test d'ajout, de modification et de suppression des athletes :"); Console.WriteLine("Test d'ajout, de modification et de suppression des athletes :");
var picture = "https://davidalmeida.site/assets/me_avatar.f77af006.png"; var picture = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
// Ajout d'un nouveau livre // Ajout d'un nouveau livre
var newAthlete = new AthleteEntity { UserName = "Doe", LastName = "Doe",ProfilPicture = picture,FirstName = "John", Email = "essaie.example.com", PasswordHash = "TheNewPassword", Sexe = 'M', Length = 1.80, Weight = 90, DateOfBirth = new DateOnly(2024, 02, 22), IsCoach = false }; var newAthlete = new AthleteEntity { Username = "Doe", LastName = "Doe",ProfilPicture = picture,FirstName = "John", Email = "essaie.example.com", Password = "TheNewPassword", Sexe = "M", Length = 1.80, Weight = 90, DateOfBirth = new DateOnly(2024, 02, 22), IsCoach = false };
db.AthletesSet.Add(newAthlete); db.AthletesSet.Add(newAthlete);
db.SaveChanges(); db.SaveChanges();
@ -763,7 +763,7 @@ class Program
Console.WriteLine("Athlete après ajout :"); Console.WriteLine("Athlete après ajout :");
foreach (var athlete in db.AthletesSet) foreach (var athlete in db.AthletesSet)
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
// Modification du titre du nouveau livre // Modification du titre du nouveau livre
@ -774,7 +774,7 @@ class Program
Console.WriteLine("Livres après modification :"); Console.WriteLine("Livres après modification :");
foreach (var athlete in db.AthletesSet) foreach (var athlete in db.AthletesSet)
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
// Suppression du nouveau livre // Suppression du nouveau livre
@ -785,7 +785,7 @@ class Program
Console.WriteLine("Livres après suppression :"); Console.WriteLine("Livres après suppression :");
foreach (var athlete in db.AthletesSet) foreach (var athlete in db.AthletesSet)
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
} }

@ -5,6 +5,13 @@
<ProjectReference Include="..\..\StubbedContextLib\StubbedContextLib.csproj" /> <ProjectReference Include="..\..\StubbedContextLib\StubbedContextLib.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>

@ -96,7 +96,7 @@ class Program
foreach (var athlete in dataSource.Athletes) foreach (var athlete in dataSource.Athletes)
{ {
Console.WriteLine($"\t\t\t{athlete.Id} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t\t\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}");
} }
} }
@ -122,7 +122,7 @@ class Program
foreach (var athlete in dataSource.Athletes) foreach (var athlete in dataSource.Athletes)
{ {
Console.WriteLine($"\t\t\t{athlete.Id} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t\t\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}");
} }
} }
@ -138,7 +138,7 @@ class Program
foreach (var athlete in db.AthletesSet.Include(a => a.Statistics).Include(a => a.Activities).Include(a => a.TrainingsAthlete).Include(a => a.NotificationsSent).Include(a => a.DataSource).Include(a => a.TrainingsCoach).Include(a => a.NotificationsReceived)) foreach (var athlete in db.AthletesSet.Include(a => a.Statistics).Include(a => a.Activities).Include(a => a.TrainingsAthlete).Include(a => a.NotificationsSent).Include(a => a.DataSource).Include(a => a.TrainingsCoach).Include(a => a.NotificationsReceived))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}");
Console.WriteLine("\t\tStatistiques :"); Console.WriteLine("\t\tStatistiques :");
Console.WriteLine("\t\t---------------------------------"); Console.WriteLine("\t\t---------------------------------");
@ -199,9 +199,9 @@ class Program
Console.WriteLine("Accès à l'athlète d'id '2' :"); Console.WriteLine("Accès à l'athlète d'id '2' :");
Console.WriteLine("---------------------------------"); Console.WriteLine("---------------------------------");
foreach (var athlete in db.AthletesSet.Where(a => a.Id == 2).Include(a => a.Statistics).Include(a => a.Activities).Include(a => a.TrainingsAthlete).Include(a => a.NotificationsSent).Include(a => a.DataSource).Include(a => a.TrainingsCoach).Include(a => a.NotificationsReceived)) foreach (var athlete in db.AthletesSet.Where(a => a.IdAthlete == 2).Include(a => a.Statistics).Include(a => a.Activities).Include(a => a.TrainingsAthlete).Include(a => a.NotificationsSent).Include(a => a.DataSource).Include(a => a.TrainingsCoach).Include(a => a.NotificationsReceived))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}");
Console.WriteLine("\t\tStatistiques :"); Console.WriteLine("\t\tStatistiques :");
Console.WriteLine("\t\t---------------------------------"); Console.WriteLine("\t\t---------------------------------");
@ -269,7 +269,7 @@ class Program
foreach (var athlete in db.AthletesSet.Include(f => f.Followers).Include(f => f.Followings)) foreach (var athlete in db.AthletesSet.Include(f => f.Followers).Include(f => f.Followings))
{ {
Console.WriteLine($"\t{athlete.Id} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}");
Console.WriteLine($""); Console.WriteLine($"");
Console.WriteLine($""); Console.WriteLine($"");

@ -1,114 +0,0 @@
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);
}
}
}
*/

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save