Compare commits

..

97 Commits

Author SHA1 Message Date
David D'ALMEIDA 271c4b18ed Merge remote-tracking branch 'origin/final'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA f213f8c8a7 text fix
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is failing Details
1 year ago
David D'ALMEIDA 6752077f43 Merge remote-tracking branch 'origin/final' into final
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 578be65f84 try to optimize
1 year ago
David D'ALMEIDA f9a8dc18b0 Mise à jour de 'README.md'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 7677bb26ae Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA acff174833 Merge remote-tracking branch 'origin/final' into final
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA d61ec723b2 typo fix
1 year ago
David D'ALMEIDA 15173c2cbc Mise à jour de '.drone.yml'
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA b7efc56fc3 Mise à jour de '.drone.yml'
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA cbc961d0e2 fix get ByID
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA f74e5dbd40 goooooodd
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 5e007ece9f changes url path
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 61ae846238 add id in the Token [:warning]
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 4578b9c4e3 fix null for dataSource
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 9cddc06343 minor update (sexe string to char)
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA c5fd7c16a0 fix deps
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
1 year ago
David D'ALMEIDA d13cae449b add missing file
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 0ae475613c normally fix conditional for auth
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 6c4c96f7fb seems to work not tested for the moment
1 year ago
David D'ALMEIDA 49110b2e47 auth working
1 year ago
David D'ALMEIDA dff77f80c5 Mise à jour de '.drone.yml'
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA fcf7182a7a Mise à jour de '.drone.yml'
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA e6c1c16f6f Make DataSourceEntity for athlete nullable so required False
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 5dfb8d03f7 ok
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 5c415991c1 what
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA ea245802a9 FINE
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 84515d656e https in string
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 04a1d7e107 try
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 2486e6b7eb how
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA cff1484c4a https
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 4e6ab80d58 end
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA d20f23478f just to see
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 3849147b2d try
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 6dd73d34a7 small change on try
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 997c06b693 try
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA ecc4079700 try
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 05c0484514 try
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 56b65ff45b swagger
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 885ddfd4a1 Swag
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 2511471ec6 Test
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA b3e1f5c602 KO
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA eb1ed934b3 OK
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 12e0bb8962 CI
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 4eb9edd4b5 CI
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 0922d1cfda CI
1 year ago
David D'ALMEIDA 25081267b9 clean
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 2f1dc9e2dd CI
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 2ac5772d2a Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 173b13add7 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 96a5dbbb9c Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA fca04ca179 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA e9088d303f CI.
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA fe29cce7e2 CI -should really Work
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 62d33ab922 CI - EnsureCreated in create service
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA f3a2c23166 CI - without ensure in Program.cs
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 084ce29b4e CI - TEST
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 605bab3241 CI TEST
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA d0cea334e3 CI - SAME NAMESPACE
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 5e5b86dbb0 ci
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 62c774b3c5 CI
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA d5dda3fa67 Merge remote-tracking branch 'origin/final' into final
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 062446a1a8 CI
1 year ago
David D'ALMEIDA 8649b76ba5 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA e727c083f5 CI
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA db99e9404f CI
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA d95d89c817 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 87a7e34ea8 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 26d5968f9a Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA d666e73964 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA a24704c787 clean
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 94b3f2054f clean
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 323dbc14e2 Merge remote-tracking branch 'origin/tests_merging' into final
1 year ago
David D'ALMEIDA c60eecfb11 clean
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 0f99118440 test
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 9b1b416fa9 ok
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 3ded27e17c make it work
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 656ceb2f9e fuck
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA e5eb1b64e0 maybe
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 39e4658c30 no comprendo
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 428e6c8565 test ci
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA ce99685034 test to fix 404
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 209ded5c08 Mise à jour de '.drone.yml'
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 8aed0d790c Entities2Dto add
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA 28418d3513 tempory ugly
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 1db4be9e0d finist but not clean
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII b769290f57 🐛 Fix namespace bug
continuous-integration/drone/push Build is passing Details
1 year ago
Antoine PEREDERII d36b6e467e Update 'src/Tests/WebAPIConsoleTests/Program.cs'
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII b0e0ef6931 Update 'src/Tests/WebAPIConsoleTests/ActivityServiceAPI.cs'
continuous-integration/drone/push Build was killed Details
1 year ago
Antoine PEREDERII 0f5c710acf Update 'src/Tests/WebAPIConsoleTests/HttpRequest.cs'
continuous-integration/drone/push Build was killed Details
1 year ago
Antoine PEREDERII cdcd64390a 📝 Uncomment code
continuous-integration/drone/push Build is passing Details
1 year ago
Antoine PEREDERII a121ec348a 🧪 Test some tests
continuous-integration/drone/push Build is passing Details
1 year ago
David D'ALMEIDA efab70f978 push
continuous-integration/drone/push Build is failing Details
1 year ago
David D'ALMEIDA 3c1510e041 yep
1 year ago
David D'ALMEIDA bd83344519 try
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII d4176ee17b 🧪 Add somes console and unit tests
continuous-integration/drone/push Build is failing Details
1 year ago
Antoine PEREDERII 7cfe9981e2 Update 'README.md'
continuous-integration/drone/push Build is passing Details
1 year ago

@ -2,12 +2,11 @@ kind: pipeline
type: docker type: docker
name: HeartTrack-API name: HeartTrack-API
trigger: #trigger:
branch: # branch:
- master # - master
event: # event:
- push # - push
steps: steps:
- name: build - name: build
image: mcr.microsoft.com/dotnet/sdk:8.0 image: mcr.microsoft.com/dotnet/sdk:8.0
@ -22,7 +21,7 @@ steps:
commands: commands:
- cd src/ - cd src/
- dotnet restore HeartTrack.sln - dotnet restore HeartTrack.sln
- dotnet test HeartTrack.sln --no-restore - dotnet test HeartTrack.sln --no-restore
depends_on: [build] depends_on: [build]
- name: code-analysis - name: code-analysis
@ -45,45 +44,44 @@ steps:
- dotnet publish HeartTrack.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release - dotnet publish HeartTrack.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release
- dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN} - dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
depends_on: [ tests ] depends_on: [ tests ]
- name: swagger - name: swagger
image: mcr.microsoft.com/dotnet/sdk:8.0 image: mcr.microsoft.com/dotnet/sdk:7.0
failure: ignore failure: ignore
volumes: volumes:
- name: docs - name: docs
path: /docs path: /docs
environment: environment:
CODEFIRST_CLIENTDRONE_ENV_DOTNET_ROLL_FORWARD: LatestMajor CODEFIRST_CLIENTDRONE_ENV_DOTNET_ROLL_FORWARD: LatestMajor
CODEFIRST_CLIENTDRONE_ENV_DOTNET_ROLL_FORWARD_TO_PRERELEASE: 1 CODEFIRST_CLIENTDRONE_ENV_DOTNET_ROLL_FORWARD_TO_PRERELEASE: 1
commands: commands:
- cd src/ - cd src/
- dotnet restore HeartTrack.sln - dotnet restore HeartTrack.sln
- cd HeartTrackAPI - cd HeartTrackAPI
- dotnet new tool-manifest - dotnet new tool-manifest
- dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli - dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
- cd ../ - cd ../
- dotnet build HeartTrack.sln -c Release --no-restore - dotnet build HeartTrack.sln -c Release --no-restore
- dotnet publish HeartTrack.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release - dotnet publish HeartTrack.sln -c Release --no-restore -o CI_PROJECT_DIR/build/release
- export PATH="$PATH:/root/.dotnet/tools" - export PATH="$PATH:/root/.dotnet/tools"
- swagger tofile --output /docs/swagger.json HeartTrackAPI/bin/Release/net8.0/HeartTrackAPI.dll v1 - swagger tofile --output /docs/swagger.json HeartTrackAPI/bin/Release/net8.0/HeartTrackAPI.dll v1
depends_on: [build,tests] depends_on: [build,tests]
- name: generate-and-deploy-docs - name: generate-and-deploy-docs
image: hub.codefirst.iut.uca.fr/maxime.batista/codefirst-docdeployer image: hub.codefirst.iut.uca.fr/maxime.batista/codefirst-docdeployer
failure: ignore failure: ignore
commands: commands:
- /entrypoint.sh -l docs/doxygen -t doxygen - /entrypoint.sh -l docs/doxygen -t doxygen
when: when:
event: event:
- push - push
depends_on: [ build ] depends_on: [ build ]
volumes: volumes:
- name: docs - name: docs
temp: {} temp: {}
---
---
kind: pipeline kind: pipeline
type: docker type: docker
@ -92,69 +90,98 @@ name: HeartTrack-API-CD
trigger: trigger:
event: event:
- push - push
steps: steps:
- name: docker-build-and-push - name: docker-build-and-push
image: plugins/docker image: plugins/docker
settings: settings:
dockerfile: src/HeartTrackAPI/Dockerfile dockerfile: src/HeartTrackAPI/Dockerfile
context: src/ context: src/
registry: hub.codefirst.iut.uca.fr registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/david.d_almeida/api repo: hub.codefirst.iut.uca.fr/david.d_almeida/api
username: username:
from_secret: SECRET_REGISTRY_USERNAME from_secret: SECRET_REGISTRY_USERNAME
password: password:
from_secret: SECRET_REGISTRY_PASSWORD from_secret: SECRET_REGISTRY_PASSWORD
# database container stub # database container stub
- name: deploy-container-stub - name: deploy-container-stub
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment: environment:
CODEFIRST_CLIENTDRONE_ENV_TYPE: STUB CODEFIRST_CLIENTDRONE_ENV_TYPE: STUB
IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest
CONTAINERNAME: heart_stub CONTAINERNAME: heart_stub
COMMAND: create COMMAND: create
ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond,marcchevaldonne ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond,camillepetitalot
OVERWRITE: true OVERWRITE: true
depends_on: [ docker-build-and-push ] depends_on: [ docker-build-and-push ]
# - name: deploy-container
# image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
# environment:
# IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/api:latest
# CONTAINERNAME: heart_api
# CODEFIRST_CLIENTDRONE_ENV_TYPE: API
# CODEFIRST_CLIENTDRONE_ENV_PORT: 8080
# ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolas.raymond
# COMMAND: create
# OVERWRITE: true
# 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
environment: environment:
IMAGENAME: mariadb:10 IMAGENAME: mariadb:10
CONTAINERNAME: mysql CONTAINERNAME: mysql
COMMAND: create COMMAND: create
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: from_secret: db_database
from_secret: db_database CODEFIRST_CLIENTDRONE_ENV_MARIADB_USER:
CODEFIRST_CLIENTDRONE_ENV_MARIADB_USER: from_secret: db_user
from_secret: db_user CODEFIRST_CLIENTDRONE_ENV_MARIADB_PASSWORD:
CODEFIRST_CLIENTDRONE_ENV_MARIADB_PASSWORD: from_secret: db_password
from_secret: db_password ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond,camillepetitalot
ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond,camillepetitalot
# database container bdd
# database container bdd
- name: deploy-container-bdd - name: deploy-container-bdd
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment: environment:
CODEFIRST_CLIENTDRONE_ENV_TYPE: BDD CODEFIRST_CLIENTDRONE_ENV_TYPE: BDD
CODEFIRST_CLIENTDRONE_ENV_HOST: HeartDev-mysql CODEFIRST_CLIENTDRONE_ENV_HOST: HeartDev-mysql
CODEFIRST_CLIENTDRONE_ENV_PORTDB: 3306 CODEFIRST_CLIENTDRONE_ENV_PORTDB: 3306
CODEFIRST_CLIENTDRONE_ENV_DATABASE: CODEFIRST_CLIENTDRONE_ENV_DATABASE:
from_secret: db_database from_secret: db_database
CODEFIRST_CLIENTDRONE_ENV_USERNAME: CODEFIRST_CLIENTDRONE_ENV_USERNAME:
from_secret: db_user from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_PASSWORD: CODEFIRST_CLIENTDRONE_ENV_PASSWORD:
from_secret: db_password from_secret: db_password
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,camillepetitalot ADMINS: davidd_almeida,kevinmonteiro,antoineperederii,paullevrault,antoinepinagot,nicolasraymond
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]

@ -6,9 +6,11 @@
</div> </div>
<div align = center> <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; ![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; ![Entity Framework](https://img.shields.io/badge/Entity_Framework-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)
@ -29,7 +31,7 @@
</div> </div>
# Table des matières # Table des matières
[Présentation](#présentation) | [Répartition du Git](#répartition-du-git) | [Documentation](#documentation) | [Prérequis](#prerequisites) | [Pour commencer](#getting-started) | [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](#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)
@ -40,6 +42,7 @@
### Contexte ### 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. 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 ### Récapitulatif du Projet
@ -48,10 +51,12 @@ Le projet HeartTrack, avec son application HeartTrack, vise à offrir une soluti
## Répartition du Git ## Répartition du Git
[**Sources**](src/) : **Code de l'application** [**Sources**](Sources/) : **Code de l'application**
[**Documents**](docs/Diagramme/README_DIAGRAMMES.md) : **Documentation de l'application et diagrammes** [**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 : 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 :
@ -76,56 +81,54 @@ Documentation et informations à propos de `HearthTrack` disponible [ici](https:
* [![Git](https://img.shields.io/badge/Versioning-Git-000?style=for-the-badge&logo=git&logoColor=white&color=red)](https://git-scm.com/) * [![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 ## Getting Started
Lancer le projet via le projet HeartTrackAPI afin de démarrer la base de donnée pour swagger en http.
Les méthodes PUT sont en cours de production. Les autres routes sont finalisés et en cours de tests.
## Ce que nous avons fait ## Ce que nous avons fait
### Entity Framework ### Entity Framework
réalisé | niveau | description | coeff | jalon réalisé | niveau | description | coeff | jalon
--- | --- | --- | --- | --- --- | --- | --- | --- | ---
| ☢️ | Le dépôt doit être accessible par l'enseignant | ☢️ | J1 [ ] | ☢️ | Le dépôt doit être accessible par l'enseignant | ☢️ | J1
| ☢️ | un .gitignore doit exister au premier push | ☢️ | J1 [ ] | ☢️ | un .gitignore doit exister au premier push | ☢️ | J1
| 🎬 | les *projets* et les tests compilent | 1 | J1 & J2 [ ] | 🎬 | 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 [ ] | 🎬 | 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 [ ] | 🟢 | Transcription du modèle : Modèle vers entités (et inversement) | 2 | J1
| 🟢 | Requêtes CRUD simples (sur une table) | 1 | J1 [ ] | 🟢 | Requêtes CRUD simples (sur une table) | 1 | J1
| 🟢 | Utilisation de LINQ to Entities | 2 | J1 [ ] | 🟢 | Utilisation de LINQ to Entities | 2 | J1
| 🟡 | Injection / indépendance du fournisseur | 1 | J1 [ ] | 🟡 | Injection / indépendance du fournisseur | 1 | J1
| 🟡 | Requêtes CRUD sur des données complexes (images par exemple) | 2 | J1 [ ] | 🟡 | Requêtes CRUD sur des données complexes (images par exemple) | 2 | J1
| 🟢 | Tests - Appli Console | 1 | J1 [ ] | 🟢 | Tests - Appli Console | 1 | J1
| 🟢 | Tests - Tests unitaires (avec SQLite in memory) | 2 | J1 [ ] | 🟢 | Tests - Tests unitaires (avec SQLite in memory) | 2 | J1
| 🟢 | Tests - Données stubbées et/ou Moq | 1 | J1 [ ] | 🟢 | Tests - Données stubbées et/ou Moq | 1 | J1
| 🟡 | CI : build, tests, Sonar (doc?) | 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 [ ] | 🟡 | 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 [ ] | 🟢 | Liens avec le web service | 2 | J1
| 🟡 | Utilisation d'un *Logger* | 1 | J1 [ ] | 🟡 | Utilisation d'un *Logger* | 1 | J1
| 🟡 | Déploiement | 4 | J2 [ ] | 🟡 | Déploiement | 4 | J2
| 🔴 | Unit of Work / Repository + extras (héritage, accès concurrents...) | 8 | J2 [ ] | 🔴 | Unit of Work / Repository + extras (héritage, accès concurrents...) | 8 | J2
| 🟢 | Utilisation dans le projet | 2 | J2 [ ] | 🟢 | Utilisation dans le projet | 2 | J2
| 🟢 | mon dépôt possède un readme qui apporte quelque chose... | 2 | J2 [ ] | 🟢 | mon dépôt possède un readme qui apporte quelque chose... | 2 | J2
### API ### API
réalisé | niveau | description | coeff | jalon réalisé | niveau | description | coeff | jalon
--- | --- | --- | --- | --- --- | --- | --- | --- | ---
| ☢️ | Le dépôt doit être accessible par l'enseignant | ☢️ | J1 [ ] | ☢️ | Le dépôt doit être accessible par l'enseignant | ☢️ | J1
| ☢️ | un .gitignore doit exister au premier push | ☢️ | J1 [ ] | ☢️ | un .gitignore doit exister au premier push | ☢️ | J1
| 🎬 | les *projets* et les tests compilent | 1 | J1 & J2 [ ] | 🎬 | 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 [ ] | 🎬 | le projet et le tests s'exécutent sans bug (concernant la partie persistance) | 4 | J1 & J2
| 🟢 | Modèle <-> DTO | 1 | J1 [ ] | 🟢 | Modèle <-> DTO | 1 | J1
| 🟢 | Entities <-> DTO | 1 | J1 [ ] | 🟢 | Entities <-> DTO | 1 | J1
| 🟡 | Authentification | 4 | 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 [ ] | 🟢 | 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 [ ] | 🟡 | Pagination & filtrage | 2 | J1
| 🟢 | Injection de service | 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 [ ] | 🟡 | 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 - Appli Console (consommation des requêtes) | 4 | J1
| 🟢 | Tests - Tests unitaires (avec Stub et/ou Moq) | 2 | J1 [ ] | 🟢 | Tests - Tests unitaires (avec Stub et/ou Moq) | 2 | J1
| 🟡 | CI : build, tests, Sonar, Documentation (en particulier Swagger avec exemples...) | 1 | J1 [ ] | 🟡 | CI : build, tests, Sonar, Documentation (en particulier Swagger avec exemples...) | 1 | J1
| 🟢 | Liens avec la persistance en base de données | 4 | J1 [ ] | 🟢 | Liens avec la persistance en base de données | 4 | J1
| 🟡 | Utilisation d'un *Logger* | 1 | J1 [ ] | 🟡 | Utilisation d'un *Logger* | 1 | J1
| 🟡 | Déploiement | 4 | J2 [ ] | 🟡 | Déploiement | 4 | J2
❌ | 🟡 | Utilisation dans le projet | 4 | J2 ❌ | 🟡 | Utilisation dans le projet | 4 | J2
✅ | 🎬 | mon dépôt possède un readme qui apporte quelque chose... | 1 | J2 ✅ | 🎬 | mon dépôt possède un readme qui apporte quelque chose... | 1 | J2
## Fabriqué avec ## Fabriqué avec
![.NET](https://img.shields.io/badge/Langage-.NET-000?style=for-the-badge&logo=.net&logoColor=white&color=blue) ![.NET](https://img.shields.io/badge/Langage-.NET-000?style=for-the-badge&logo=.net&logoColor=white&color=blue)

@ -1,4 +1,6 @@
using Dto; using Dto;
using Dto.Tiny;
using Entities;
using Model; using Model;
using Shared; using Shared;
@ -7,7 +9,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, User user)
{ {
Func<ActivityDto, Activity> create = activity => new Activity Func<ActivityDto, Activity> create = activity => new Activity
@ -72,6 +75,28 @@ public static class ActivityMapper
return model.ToU(_mapper, create, link); return model.ToU(_mapper, create, link);
} }
public static ActivityEntity ToEntity(this ActivityTinyDto tinyDto)
{
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) public static IEnumerable<Activity> ToModels(this IEnumerable<ActivityDto> dtos, User user)
=> dtos.Select(dto => dto.ToModel(user)); => dtos.Select(dto => dto.ToModel(user));

@ -18,7 +18,6 @@ public static class HeartRateMapper
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

@ -7,6 +7,8 @@
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
using Entities; using Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DbContextLib namespace DbContextLib
@ -14,7 +16,7 @@ namespace DbContextLib
/// <summary> /// <summary>
/// Represents the database context for the FitnessApp. /// Represents the database context for the FitnessApp.
/// </summary> /// </summary>
public class HeartTrackContext : DbContext public class HeartTrackContext : IdentityDbContext<AthleteEntity,IdentityRole<int>,int>
{ {
/// <summary> /// <summary>
/// Gets or sets the set of athletes. /// Gets or sets the set of athletes.
@ -132,10 +134,10 @@ namespace DbContextLib
//primary key of AthleteEntity //primary key of AthleteEntity
modelBuilder.Entity<AthleteEntity>() modelBuilder.Entity<AthleteEntity>()
.HasKey(at => at.IdAthlete); .HasKey(at => at.Id);
//generation mode (at insertion) //generation mode (at insertion)
modelBuilder.Entity<AthleteEntity>() modelBuilder.Entity<AthleteEntity>()
.Property(at => at.IdAthlete) .Property(at => at.Id)
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
// add image column type // add image column type
// modelBuilder.Entity<AthleteEntity>() // modelBuilder.Entity<AthleteEntity>()
@ -168,16 +170,16 @@ namespace DbContextLib
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
modelBuilder.Entity<FriendshipEntity>() modelBuilder.Entity<FriendshipEntity>()
.HasKey(f => new { f.FollowingId, f.FollowerId }); .HasKey(f => new { f.FollowerId, f.FollowingId });
modelBuilder.Entity<FriendshipEntity>() modelBuilder.Entity<FriendshipEntity>()
.HasOne(fing => fing.Following) .HasOne(fing => fing.Following)
.WithMany(fings => fings.Followings) .WithMany(fings => fings.Followers)
.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.Followers) .WithMany(fers => fers.Followings)
.HasForeignKey(fing => fing.FollowerId); .HasForeignKey(fing => fing.FollowerId);
// ! // !
@ -233,16 +235,24 @@ 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(); .IsRequired(false);
List<IdentityRole<int>> roles =
// modelBuilder.Entity<AthleteEntity>() [
// .HasMany(fer => fer.Followers) new()
// .WithMany(fing => fing.Followings) {
// .UsingEntity<FriendshipEntity>( Id = 1,
// l => l.HasOne<AthleteEntity>().WithMany().HasForeignKey(fer => fer.FollowerId), Name = "Athlete",
// r => r.HasOne<AthleteEntity>().WithMany().HasForeignKey(fing => fing.FollowingId), NormalizedName = "ATHLETE"
// j => j.Property(f => f.StartDate).HasDefaultValueSql("CURRENT_TIMESTAMP") },
// );
new()
{
Id = 2,
Name = "Coach",
NormalizedName = "COACH"
}
];
modelBuilder.Entity<IdentityRole<int>>().HasData(roles);
} }
} }
} }

@ -1,25 +0,0 @@
using Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace DbContextLib.Identity;
public class AuthDbContext: IdentityDbContext<IdentityUser>
{
public AuthDbContext(DbContextOptions<AuthDbContext> options) : base(options) { }
public AuthDbContext() { }
/*
/// <summary>
/// Configures the database options if they are not already configured.
/// </summary>
/// <param name="optionsBuilder">The options builder instance.</param>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlite($"Data Source=uca.HeartTrack.db");
}
}*/
}

@ -12,7 +12,7 @@ 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 string Sexe { get; set; } public required char 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; }

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace Dto.Auth;
public class LoginRequestDto
{
[Required(ErrorMessage = "Username is required")]
public string Username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
}

@ -0,0 +1,23 @@
namespace Dto.Auth;
public class AuthResponseDto
{
/// <summary>
/// Gets or sets the access token issued by the OAuth provider.
/// </summary>
public string AccessToken { get; set; }
/// <summary>Gets or sets the token type.</summary>
/// <remarks>Typically the string “bearer”.</remarks>
public string? TokenType { get; set; }
/// <summary>
/// Gets or sets a refresh token that applications can use to obtain another access token if tokens can expire.
/// </summary>
public string? RefreshToken { get; set; }
/// <summary>
/// Gets or sets the validatity lifetime of the token in seconds.
/// </summary>
public string? ExpiresIn { get; set; }
}

@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
namespace Dto.Auth;
public class RegisterRequestDto
{
[MaxLength(100)]
[Required(ErrorMessage = "Username is required")]
public string Username { get; set; }
[MaxLength(150)]
[Required(ErrorMessage = "LastName is required")]
public string LastName { get; set; }
[MaxLength(100)]
[Required(ErrorMessage = "FirstName is required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Email is required")]
[EmailAddress]
public string Email { get; set; }
[Required(ErrorMessage = "Sexe is required")]
public char Sexe { get; set; }
[Required(ErrorMessage = "Size is required")]
public float Size { get; set; }
[Required(ErrorMessage = "Weight is required")]
public float Weight { get; set; }
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
[Required(ErrorMessage = "DateOfBirth is required")]
public DateTime DateOfBirth { get; set; }
public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
[Required(ErrorMessage = "Role is required")]
public bool IsCoach { get; set; }
}

@ -12,11 +12,9 @@ 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; }
} }

@ -0,0 +1,12 @@
using Dto.Tiny;
namespace Dto;
public class NewActivityDto
{
public ActivityTinyDto Activity { get; set; }
public HeartRateTinyDto[]? HeartRates { get; set; }
public int? DataSourceId { get; set; }
public int AthleteId { get; set; }
}

@ -0,0 +1,25 @@
using Dto.Tiny;
namespace Dto;
public class ResponseActivityDto
{
public int Id { get; set; }
public string Type { get; set; } = "";
public DateTime Date { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public int EffortFelt { get; set; }
public float Variability { get; set; }
public float Variance { get; set; }
public float StandardDeviation { get; set; }
public float Average { get; set; }
public int Maximum { get; set; }
public int Minimum { get; set; }
public float AverageTemperature { get; set; }
public bool HasAutoPause { get; set; }
public HeartRateTinyDto[]? HeartRates { get; set; }
public DataSourceTinyDto? DataSource { get; set; }
public UserTinyDto? Athlete { get; set; }
}

@ -0,0 +1,18 @@
using Dto.Tiny;
namespace Dto;
public class ResponseDataSourceDto
{
public int Id { get; set; }
public string Type { get; set; } = "Unknown";
public string Model { get; set; }
public float Precision { get; set; }
public ActivityTinyDto[]? Activities { get; set; }
public UserTinyDto[]? Users { get; set; }
}

@ -0,0 +1,28 @@
using System.ComponentModel.DataAnnotations;
using Dto.Tiny;
namespace Dto;
public class ResponseUserDto
{
public int Id { get; set; }
[MaxLength(100)]
public required string Username { get; set; }
[MaxLength(150)]
public required string LastName { get; set; }
[MaxLength(100)]
public required string FirstName { get; set; }
public required string Email { get; set; }
public required char Sexe { get; set; }
public float Lenght { get; set; }
public float Weight { get; set; }
public string? Password { get; set; }
public DateTime DateOfBirth { get; set; }
public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
public bool IsCoach { get; set; }
public LargeImageDto? Image { get; set; }
public ActivityTinyDto[] Activities { get; set; }
public DataSourceTinyDto? DataSource { get; set; }
public FriendshipDto?[] Followers { get; set; }
public FriendshipDto?[] Followings { get; set; }
}

@ -0,0 +1,19 @@
namespace Dto.Tiny;
public class ActivityTinyDto
{
public int? Id { get; set; }
public string Type { get; set; } = "";
public DateTime Date { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public int EffortFelt { get; set; }
public float Variability { get; set; }
public float Variance { get; set; }
public float StandardDeviation { get; set; }
public float Average { get; set; }
public int Maximum { get; set; }
public int Minimum { get; set; }
public float AverageTemperature { get; set; }
public bool HasAutoPause { get; set; }
}

@ -0,0 +1,12 @@
namespace Dto.Tiny;
public class DataSourceTinyDto
{
public int Id { get; set; }
public string Type { get; set; } = "Unknown";
public string Model { get; set; }
public float Precision { get; set; }
}

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

@ -0,0 +1,16 @@
namespace Dto.Tiny;
public class HeartRateTinyDto
{
public int Id { get; set; }
public DateTime Timestamp { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public double? Altitude { get; set; }
public int HeartRate { get; set; }
public int? Cadence { get; set; }
public double? Distance { get; set; }
public double? Speed { get; set; }
public int? Power { get; set; }
public double? Temperature { get; set; }
}

@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace Dto.Tiny;
public class UserTinyDto
{
public int Id { get; set; }
[MaxLength(100)]
public required string Username { get; set; }
[MaxLength(150)]
public required string LastName { get; set; }
[MaxLength(100)]
public required string FirstName { get; set; }
public required string Email { get; set; }
public required char Sexe { get; set; }
public float Length { get; set; }
public float Weight { get; set; }
public string? Password { get; set; }
public DateTime DateOfBirth { get; set; }
public string ProfilePicture { get; set; } = "https://davidalmeida.site/assets/me_avatar.f77af006.png";
public bool IsCoach { get; set; }
}

@ -9,18 +9,23 @@ 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.IdAthlete, Id = athleteEntity.Id,
FirstName = athleteEntity.FirstName, FirstName = athleteEntity.FirstName,
LastName = athleteEntity.LastName, LastName = athleteEntity.LastName,
Email = athleteEntity.Email, Email = athleteEntity.Email,
MotDePasse = athleteEntity.Password, MotDePasse = athleteEntity.PasswordHash,
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,
@ -42,13 +47,13 @@ public static class UserMappeur
{ {
Func<User, AthleteEntity> create = user => new AthleteEntity Func<User, AthleteEntity> create = user => new AthleteEntity
{ {
IdAthlete = user.Id, Id = 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,
Password = user.MotDePasse, PasswordHash = 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,
@ -62,17 +67,6 @@ 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);

@ -105,6 +105,6 @@ namespace Entities
public int AthleteId { get; set; } public int AthleteId { get; set; }
public AthleteEntity Athlete { get; set; } = null!; public AthleteEntity Athlete { get; set; }
} }
} }

@ -8,56 +8,58 @@
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 public class AthleteEntity : IdentityUser<int>
{ {
/// <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 int IdAthlete { get; set; } public override int Id { 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 required string Username { get; set; } public override 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 required string LastName { get; set; } public 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 required string FirstName { get; set; } public 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 required string Email { get; set; } public override 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 required string Sexe { get; set; } public char Sexe { get; set; }
/// <summary> /// <summary>
/// Gets or sets the height of the athlete. /// Gets or sets the height of the athlete.
@ -68,12 +70,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 Password is ")] [Required(ErrorMessage = "Athlete PasswordHash is ")]
public required string Password { get; set; } public string PasswordHash { get; set; }*/
/// <summary> /// <summary>
/// Gets or sets the date of birth of the athlete. /// Gets or sets the date of birth of the athlete.
@ -87,7 +89,6 @@ namespace Entities
/// </summary> /// </summary>
public bool IsCoach { get; set; } public bool IsCoach { get; set; }
// [TODO] [DAVE] Check Image
public string? ProfilPicture { get; set; } public string? ProfilPicture { get; set; }
public LargeImageEntity? Image { get; set; } public LargeImageEntity? Image { get; set; }

@ -5,4 +5,10 @@
<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>

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

@ -0,0 +1,70 @@
using Dto;
using Dto.Tiny;
using Entities;
using Shared;
namespace Entities2Dto;
public static class DataSourceMapper
{
private static GenericMapper<DataSourceTinyDto, DataSourceEntity> _mapper = new();
private static GenericMapper<ResponseDataSourceDto, DataSourceEntity> _mapperFull = new();
public static void Reset()
{
_mapper.Reset();
_mapperFull.Reset();
}
public static DataSourceTinyDto ToTinyDto(this DataSourceEntity entity)
{
Func<DataSourceEntity, DataSourceTinyDto> create = dataSourceEntity => new DataSourceTinyDto
{
Id = dataSourceEntity.IdSource,
Type = dataSourceEntity.Type,
Model = dataSourceEntity.Model,
Precision = dataSourceEntity.Precision
};
return entity.ToT(_mapper, create, null,false);
}
public static DataSourceEntity ToEntity(this DataSourceTinyDto dto)
{
Func<DataSourceTinyDto, DataSourceEntity> create = dataSource => new DataSourceEntity
{
IdSource = dataSource.Id,
Type = dataSource.Type,
Model = dataSource.Model,
Precision = dataSource.Precision
};
return dto.ToU(_mapper, create);
}
public static ResponseDataSourceDto ToResponseDto(this DataSourceEntity entity)
{
Func<DataSourceEntity, ResponseDataSourceDto> create = dataSourceEntity => new ResponseDataSourceDto
{
Id = dataSourceEntity.IdSource,
Type = dataSourceEntity.Type,
Model = dataSourceEntity.Model,
Precision = dataSourceEntity.Precision,
};
Action<DataSourceEntity, ResponseDataSourceDto> linker = (dataSourceEntity, dto) =>
{
dto.Activities = dataSourceEntity.Activities.ToTinyDtos().ToArray();
dto.Users = dataSourceEntity.Athletes.ToTinyDtos().ToArray();
};
return entity.ToT(_mapperFull, create, linker, false);
}
public static IEnumerable<DataSourceTinyDto> ToTinyDtos(this IEnumerable<DataSourceEntity> entities)
=> entities.Select(e => e.ToTinyDto());
public static IEnumerable<DataSourceEntity> ToEntities(this IEnumerable<DataSourceTinyDto> dtos)
=> dtos.Select(d => d.ToEntity());
}

@ -0,0 +1,20 @@
<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>

@ -0,0 +1,32 @@
using Dto.Tiny;
using Entities;
using Shared;
namespace Entities2Dto;
public static class FriendshipMapper
{
private static GenericMapper<FriendshipDto, FriendshipEntity> _mapper = new();
public static void Reset()
{
_mapper.Reset();
}
public static FriendshipDto ToTinyDto(this FriendshipEntity entity)
{
Func<FriendshipEntity, FriendshipDto> create = friendshipEntity => new FriendshipDto
{
FollowedId = friendshipEntity.FollowingId,
FollowerId = friendshipEntity.FollowerId,
};
return entity.ToT(_mapper, create, null, false);
}
public static IEnumerable<FriendshipDto> ToTinyDtos(this IEnumerable<FriendshipEntity> entities)
=> entities.Select(e => e.ToTinyDto());
}

@ -0,0 +1,63 @@
using Dto.Tiny;
using Entities;
using Shared;
namespace Entities2Dto;
public static class HeartRateMapper
{
private static GenericMapper<HeartRateTinyDto, HeartRateEntity> _mapper = new();
public static void Reset()
{
_mapper.Reset();
}
public static HeartRateTinyDto ToTinyDto(this HeartRateEntity entity)
{
var activityTmp = new DateTime();
Func<HeartRateEntity, HeartRateTinyDto> create = heartRateEntity => new HeartRateTinyDto
{
Id = heartRateEntity.IdHeartRate,
HeartRate = heartRateEntity.Bpm,
Timestamp = activityTmp,
Latitude = heartRateEntity.Latitude,
Longitude = heartRateEntity.Longitude,
Altitude = heartRateEntity.Altitude,
Cadence = heartRateEntity.Cadence,
Distance = heartRateEntity.Distance,
Speed = heartRateEntity.Speed,
Power = heartRateEntity.Power,
Temperature = heartRateEntity.Temperature
};
return entity.ToT(_mapper, create, null, false);
}
public static HeartRateEntity ToEntity(this HeartRateTinyDto dto)
{
Func<HeartRateTinyDto, HeartRateEntity> create = heartRate => new HeartRateEntity
{
IdHeartRate = heartRate.Id,
Bpm = heartRate.HeartRate,
Time = TimeOnly.FromDateTime(heartRate.Timestamp),
Latitude = heartRate.Latitude,
Longitude = heartRate.Longitude,
Altitude = heartRate.Altitude,
Cadence = heartRate.Cadence,
Distance = heartRate.Distance,
Speed = heartRate.Speed,
Power = heartRate.Power,
Temperature = heartRate.Temperature
};
return dto.ToU(_mapper, create);
}
public static IEnumerable<HeartRateTinyDto> ToTinyDtos(this IEnumerable<HeartRateEntity> entities)
=> entities.Select(e => e.ToTinyDto());
public static IEnumerable<HeartRateEntity> ToEntities(this IEnumerable<HeartRateTinyDto> dtos)
=> dtos.Select(d => d.ToEntity());
}

@ -0,0 +1,32 @@
using Dto;
using Entities;
using Shared;
namespace Entities2Dto;
public static class LargeImageMapper
{
private static GenericMapper<LargeImageDto, LargeImageEntity> _mapper = new();
public static void Reset()
{
_mapper.Reset();
}
public static LargeImageDto ToDto(this LargeImageEntity entity)
{
Func<LargeImageEntity, LargeImageDto> create = largeImageEntity => new() { Base64 = largeImageEntity.Base64 };
return entity.ToT(_mapper, create, null, false);
}
public static LargeImageEntity ToEntity(this LargeImageDto dto)
{
Func<LargeImageDto, LargeImageEntity> create = largeImage => new LargeImageEntity
{
Base64 = largeImage.Base64
};
return dto.ToU(_mapper, create);
}
}

@ -0,0 +1,102 @@
using Dto;
using Dto.Tiny;
using Entities;
using Shared;
namespace Entities2Dto;
public static class UserMappeur
{
private static GenericMapper<UserTinyDto, AthleteEntity> _mapper = new();
private static GenericMapper<ResponseUserDto, AthleteEntity> _mapperFull = new();
public static void Reset()
{
_mapper.Reset();
_mapperFull.Reset();
}
public static UserTinyDto ToTinyDto(this AthleteEntity entity)
{
Func<AthleteEntity, UserTinyDto> create = athleteEntity => new UserTinyDto
{
Id = athleteEntity.Id,
FirstName = athleteEntity.FirstName,
LastName = athleteEntity.LastName,
Email = athleteEntity.Email,
Password = athleteEntity.PasswordHash,
DateOfBirth = athleteEntity.DateOfBirth.ToDateTime(TimeOnly.MinValue),
Sexe = athleteEntity.Sexe,
Username = athleteEntity.UserName,
Weight = athleteEntity.Weight,
Length = (float)athleteEntity.Length,
ProfilePicture = athleteEntity.ProfilPicture ?? "",
IsCoach = athleteEntity.IsCoach
};
return entity.ToT(_mapper, create, null, false);
}
public static AthleteEntity ToEntity(this UserTinyDto model)
{
Func<UserTinyDto, AthleteEntity> create = user => new AthleteEntity
{
Id = user.Id,
UserName = user.Username,
Sexe = user.Sexe,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
PasswordHash = user.Password ?? "",
DateOfBirth = DateOnly.FromDateTime(user.DateOfBirth),
IsCoach = user.IsCoach,
Weight = user.Weight,
Length = user.Length,
ProfilPicture = user.ProfilePicture
};
return model.ToU(_mapper, create);
}
public static ResponseUserDto ToResponseDto(this AthleteEntity entity)
{
Func<AthleteEntity, ResponseUserDto> creator = athleteEntity => new ResponseUserDto
{
Id = athleteEntity.Id,
FirstName = athleteEntity.FirstName,
LastName = athleteEntity.LastName,
Email = athleteEntity.Email,
Password = athleteEntity.PasswordHash,
DateOfBirth = athleteEntity.DateOfBirth.ToDateTime(TimeOnly.MinValue),
Sexe = athleteEntity.Sexe,
Username = athleteEntity.UserName,
Weight = athleteEntity.Weight,
Lenght = (float)athleteEntity.Length,
ProfilePicture = athleteEntity.ProfilPicture ?? "",
IsCoach = athleteEntity.IsCoach,
};
Action<AthleteEntity, ResponseUserDto> linker = (athleteEntity, userDto) =>
{
userDto.Activities = athleteEntity.Activities.ToTinyDtos().ToArray();
userDto.Image = athleteEntity.Image?.ToDto();
userDto.DataSource = athleteEntity.DataSource?.ToTinyDto();
userDto.Followers = athleteEntity.Followers.ToTinyDtos().ToArray();
userDto.Followings = athleteEntity.Followings.ToTinyDtos().ToArray();
};
return entity.ToT(_mapperFull, creator, linker, false);
}
public static IEnumerable<UserTinyDto> ToTinyDtos(this IEnumerable<AthleteEntity> entities)
=> entities.Select(e => e.ToTinyDto());
public static IEnumerable<AthleteEntity> ToEntities(this IEnumerable<UserTinyDto> models)
=> models.Select(m => m.ToEntity());
}

@ -23,8 +23,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csp
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}") = "ClientTests", "Tests\TestsAPI\ClientTests\ClientTests.csproj", "{9E4D3AC5-E6CA-4753-BD96-BF5EE793931A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{30AB7FAA-6072-40B6-A15E-9188B59144F9}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestApi", "Tests\TestsAPI\UnitTestApi\UnitTestApi.csproj", "{E515C8B6-6282-4D8B-8523-7B3A13E4AF58}"
@ -43,6 +41,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "APIMappers", "APIMappers\AP
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsModel", "Tests\UnitTestsModel\UnitTestsModel.csproj", "{508D380F-145C-437E-A7DF-7A17C526B2F3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsModel", "Tests\UnitTestsModel\UnitTestsModel.csproj", "{508D380F-145C-437E-A7DF-7A17C526B2F3}"
EndProject 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
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -81,10 +85,6 @@ 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
@ -121,6 +121,12 @@ Global
{508D380F-145C-437E-A7DF-7A17C526B2F3}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{508D380F-145C-437E-A7DF-7A17C526B2F3}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -129,11 +135,11 @@ Global
{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} {508D380F-145C-437E-A7DF-7A17C526B2F3} = {2B227C67-3BEC-4A83-BDA0-F3918FBC0D18}
{D0EE112F-3151-4C28-A6EC-B1CEC7883FAE} = {30FC2BE9-7397-445A-84AD-043CE70F4281}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0F3487F4-66CA-4034-AC66-1BC899C9B523} SolutionGuid = {0F3487F4-66CA-4034-AC66-1BC899C9B523}

@ -1,9 +1,10 @@
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;
@ -12,25 +13,29 @@ 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 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; _userRepository = dataManager.UserRepo;
_dataSourceRepository = dataManager.DataSourceRepo;
_logger = logger; _logger = logger;
} }
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(PageResponse<ActivityDto>), 200)] [ProducesResponseType(typeof(PageResponse<ActivityTinyDto>), 200)]
[ProducesResponseType(400)] [ProducesResponseType(400)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<ActivityDto>>> GetActivities([FromQuery] PageRequest pageRequest) public async Task<ActionResult<PageResponse<ActivityTinyDto>>> GetActivities([FromQuery] PageRequest pageRequest)
{ {
try try
{ {
@ -46,7 +51,9 @@ public class ActivityController : Controller
{ {
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)
@ -57,98 +64,52 @@ public class ActivityController : Controller
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> PostActivity(ActivityDto activityDto) public async Task<IActionResult> PostActivity(NewActivityDto activityDto)
{ {
var user = await _userRepository.GetItemById(activityDto.AthleteId); _logger.LogInformation("Executing {Action} with parameters: {Parameters}, {add}", nameof(PostActivity), activityDto.Activity.Average, activityDto.HeartRates[0].Timestamp);
var user = await _userRepository.GetUserTinyById(activityDto.AthleteId);
if (user == null) if (user == null)
{ {
_logger.LogError("Athlete with id {id} not found", activityDto.AthleteId); _logger.LogError("Athlete with id {id} not found", activityDto.AthleteId);
return NotFound($"Athlete with id {activityDto.AthleteId} not found"); return NotFound($"Athlete with id {activityDto.AthleteId} not found");
} }
var tmp = user.DataSources.FirstOrDefault(ds => ds.Id == activityDto.DataSourceId); if (activityDto.DataSourceId != null)
if (tmp == null)
{ {
_logger.LogError("DataSource with id {id} not found", activityDto.DataSourceId); var dataSource = await _dataSourceRepository.GetItemById(activityDto.DataSourceId.Value);
return NotFound($"DataSource with id {activityDto.DataSourceId} not found");
if (dataSource == null)
{
_logger.LogError("DataSource with id {id} not found", activityDto.DataSourceId);
return NotFound($"DataSource with id {activityDto.DataSourceId} not found");
}
} }
var activity = activityDto.ToModel(user); var result = await _activityService.AddActivity(activityDto);
var result = await _activityService.AddActivity(activity);
if (result == null) if (result == null)
{ {
return BadRequest(); return BadRequest();
} }
return CreatedAtAction(nameof(GetActivity), new { id = result.Id }, result.ToDto()); _logger.LogInformation("Activity added with id {id}", result.Id);
}
/*
public async Task<IActionResult> PostFitFile( Stream file, string contentType) // [FromForm]
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
ModelState.AddModelError("File",
$"The request couldn't be processed (Error 1).");
// Log error
return BadRequest(ModelState); return CreatedAtAction(nameof(GetActivity), new { id = result.Id }, result);
} }
if (file == null)
{
return BadRequest("No file was provided");
}
//var fileUploadSummary = await _fileService.UploadFileAsync(HttpContext.Request.Body, Request.ContentType);
// var activity = await _activityManager.AddActivityFromFitFile(file);
var activity = new Activity
{
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()
}
}
};
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<ActivityDto>> GetActivity(int id) public async Task<ActionResult<ResponseActivityDto>> GetActivity(int id)
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivity), id); _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivity), id);
var activity = await _activityService.GetActivityByIdAsync(id); var activity = await _activityService.GetActivityById(id);
if (activity == null) if (activity == null)
{ {
_logger.LogError("Activity with id {id} not found", id); _logger.LogError("Activity with id {id} not found", id);
return NotFound($"Activity with id {id} not found"); return NotFound($"Activity with id {id} not found");
} }
return Ok(activity.ToDto()); return Ok(activity);
} }
catch (Exception e) catch (Exception e)
{ {
@ -158,25 +119,15 @@ public class ActivityController : Controller
} }
[HttpPut("{id}")] [HttpPut("{id}")]
public async Task<IActionResult> PutActivity(int id, ActivityDto activityDto) public async Task<IActionResult> PutActivity(int id, ActivityTinyDto activityDto)
{ {
if (id != activityDto.Id) var result = await _activityService.UpdateActivity(id, activityDto);
{
return BadRequest();
}
var user = await _userRepository.GetItemById(activityDto.AthleteId);
if (user == null)
{
_logger.LogError("Athlete with id {id} not found", activityDto.AthleteId);
return NotFound($"Athlete with id {activityDto.AthleteId} not found");
}
var activity = activityDto.ToModel(user);
var result = await _activityService.UpdateActivity(id, activity);
if (result == null) if (result == null)
{ {
return NotFound(); return NotFound();
} }
return NoContent();
return Ok(result);
} }
/// <summary> /// <summary>

@ -0,0 +1,116 @@
using Dto;
using Dto.Tiny;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Model.Manager;
using Model.Repository;
using Model.utils;
namespace HeartTrackAPI.Controllers;
[ApiController]
[ApiVersion("1.0")]
[Route("api/[controller]")]
[Authorize]
public class AnalysisController : Controller
{
private readonly IActivityRepository _activityService;
private readonly ILogger<AnalysisController> _logger;
public AnalysisController(IDataManager dataManager, ILogger<AnalysisController> logger)
{
_activityService = dataManager.ActivityRepo;
_logger = logger;
}
[HttpGet("activity/{activityId}")]
public async Task<IActionResult> AnalyseByActivityId(int activityId)
{
var activity = await _activityService.GetActivityById(activityId);
if (activity == null)
{
_logger.LogInformation($"Activity with ID {activityId} not found.");
return NotFound($"Activity with ID {activityId} not found.");
}
// for the moment no need to get the user Entity [Dave]
var user = activity.Athlete;
if (user == null)
{
_logger.LogInformation($"User not found for activity ID {activityId}.");
return NotFound($"User not found for activity ID {activityId}.");
}
var analysis = ActivityAnalysis.FromActivityData(activity);
return Ok(analysis);
}
}
/*
/*
public class HeartRateZoneResult
{
public string Zone { get; set; }
public TimeSpan TimeSpent { get; set; }
}
private readonly List<HeartRateZone> _heartRateZones = new()
{
new() { Name = "Repos", MinHeartRate = 0, MaxHeartRate = 60 },
new() { Name = "Aérobie légère", MinHeartRate = 61, MaxHeartRate = 90 },
new() { Name = "Aérobie", MinHeartRate = 91, MaxHeartRate = 140 },
new() { Name = "Anaérobie", MinHeartRate = 141, MaxHeartRate = 180 },
new() { Name = "VO2 Max", MinHeartRate = 181, MaxHeartRate = 220 }
};
[HttpGet("heart-rate/zones/{activityId}")]
public IActionResult GetActivityHeartRateZones(int activityId)
{
var heartRateTinyDtos = _activityService.GetActivityById(activityId).Result?.HeartRates;
if (heartRateTinyDtos != null)
{
var heartRates = heartRateTinyDtos.ToList();
var results = _heartRateZones.Select(zone => new HeartRateZoneResult
{
Zone = zone.Name,
TimeSpent = CalculateTimeInZone(zone, heartRates)
}).ToList();
return Ok(results);
}
return NotFound("Not heart rates");
}
private TimeSpan CalculateTimeInZone(HeartRateZone zone, List<HeartRateTinyDto> heartRates)
{
var secondsInZone =
heartRates.Count(hr => hr.HeartRate >= zone.MinHeartRate && hr.HeartRate <= zone.MaxHeartRate);
return TimeSpan.FromSeconds(secondsInZone);
}* /
[HttpGet("getOptimizedPath")]
public IActionResult GetOptimizedPath(int activityId)
{
var heartRateData = GetMockHeartRateData();
var sortedData = heartRateData.OrderBy(x => x.Timestamp).ToList();
var path = new GeoJsonPath();
foreach (var item in sortedData)
{
if (item.Latitude.HasValue && item.Longitude.HasValue)
{
path.Coordinates.Add(new GeoJsonCoordinate(item.Longitude.Value, item.Latitude.Value));
}
}
return Ok(path.ToGeoJson());
}
*/

@ -0,0 +1,154 @@
using System.Globalization;
using Dto.Auth;
using Dto.Tiny;
using Entities;
using HeartTrackAPI.Request;
using HeartTrackAPI.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace HeartTrackAPI.Controllers;
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class AuthController : Controller
{
private readonly UserManager<AthleteEntity> _userManager;
private readonly ITokenService _tokenService;
private readonly SignInManager<AthleteEntity> _signinManager;
public AuthController(UserManager<AthleteEntity> userManager,ITokenService tokenService, SignInManager<AthleteEntity> signinManager)
{
_userManager = userManager;
_tokenService = tokenService;
_signinManager = signinManager;
}
[HttpPost("login")]
public async Task<IActionResult> Login(LoginRequestDto loginDto)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == loginDto.Username.ToLower());
if (user == null) return Unauthorized("Invalid username!");
var result = await _signinManager.CheckPasswordSignInAsync(user, loginDto.Password, false);
if (!result.Succeeded) return Unauthorized("Username not found and/or password incorrect");
return Ok(new AuthResponseDto
{
AccessToken = _tokenService.CreateToken(user),
ExpiresIn = DateTime.Now.AddDays(7).ToString(CultureInfo.InvariantCulture),
TokenType = "Bearer"
}
);
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterRequestDto request)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// just for testing
// the good way is to use the repository and give him the userManager
var user = new AthleteEntity
{
Email = request.Email,
UserName = request.Username,
LastName = request.LastName,
FirstName = request.FirstName,
Sexe = request.Sexe,
Length = request.Size,
Weight = request.Weight,
DateOfBirth = DateOnly.FromDateTime(request.DateOfBirth),
IsCoach = request.IsCoach
};
var createdUser = _userManager.CreateAsync(user, request.Password).Result;
if (createdUser.Succeeded)
{
var roleResult = await _userManager.AddToRoleAsync(user, request.IsCoach ? "Coach" : "Athlete");
if (roleResult.Succeeded)
{
return Ok(
new AuthResponseDto
{
AccessToken = _tokenService.CreateToken(user),
ExpiresIn = DateTime.Now.AddDays(7).ToString(),
TokenType = "Bearer"
}
);
}
{
return StatusCode(500, roleResult.Errors);
}
}
{
return StatusCode(500, createdUser.Errors);
}
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
/* var user = _userRepository.GetByEmail(request.Email);
if (user != null)
{
return BadRequest("User already exists");
}
var newUser = new User
{
Email = request.Email,
PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.PasswordHash),
FirstName = request.FirstName,
LastName = request.LastName
};
_userRepository.Add(newUser);
return Ok();*/
}
/*
[HttpPost("refresh")]
public IActionResult Refresh([FromBody] RefreshRequest request)
{
var user = _userRepository.GetByEmail(request.Email);
if (user == null)
{
return Unauthorized();
}
if (!BCrypt.Net.BCrypt.Verify(request.PasswordHash, user.PasswordHash))
{
return Unauthorized();
}
var token = _jwtService.GenerateToken(user);
return Ok(new { token });
}
*/
[HttpPost("logout")]
public IActionResult Logout()
{
return Ok();
}
/*
[HttpPost("forgot-password")]
public IActionResult ForgotPassword([FromBody] ForgotPasswordRequest request)
{
var user = _userRepository.GetByEmail(request.Email);
if (user == null)
{
return BadRequest("User not found");
}
var token = _jwtService.GenerateToken(user);
// send email with token
return Ok();
}*/
}

@ -1,8 +1,11 @@
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;
@ -17,6 +20,7 @@ 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;
@ -38,10 +42,10 @@ public class UsersController : Controller
/// <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<UserDto>), 200)] [ProducesResponseType(typeof(PageResponse<UserTinyDto>), 200)]
[ProducesResponseType(400)] [ProducesResponseType(400)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserDto>>> Get([FromQuery] PageRequest request) public async Task<ActionResult<PageResponse<UserTinyDto>>> Get([FromQuery] PageRequest request)
{ {
try try
{ {
@ -54,8 +58,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.GetUsers(request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); var athletes = await _userService.GetUsersTiny(request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false);
var pageResponse = new PageResponse<UserDto>(request.Index, request.Count, totalCount, athletes!.Select(a => a.ToDto())); var pageResponse = new PageResponse<UserTinyDto>(request.Index, request.Count, totalCount, athletes);
return Ok(pageResponse); return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
@ -74,21 +78,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(UserDto), 200)] [ProducesResponseType(typeof(ResponseUserDto), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<UserDto>> GetById([Range(0,int.MaxValue)]int id) public async Task<ActionResult<ResponseUserDto>> 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.GetItemById(id); var athlete = await _userService.GetUserById(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.ToDto()); return Ok(athlete);
} }
catch (Exception e) catch (Exception e)
{ {
@ -110,7 +114,7 @@ public class UsersController : Controller
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Count), null); _logger.LogInformation("Executing {Action}", nameof(Count));
var nbUsers = await _userService.GetNbItems(); var nbUsers = await _userService.GetNbItems();
return Ok(nbUsers); return Ok(nbUsers);
} }
@ -131,27 +135,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(UserDto), 200)] [ProducesResponseType(typeof(UserTinyDto), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<UserDto>> Update(int id, [FromBody] UserDto user) public async Task<ActionResult<UserTinyDto>> Update(int id, [FromBody] UserTinyDto 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.GetItemById(id); var athlete = await _userService.GetUserTinyById(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.UpdateItem(id, user.ToModel()); var updatedAthlete = await _userService.UpdateUser(id, user);
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.ToDto()); return Ok(updatedAthlete);
} }
catch (Exception e) catch (Exception e)
@ -178,9 +182,8 @@ public class UsersController : Controller
try try
{ {
_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);
@ -211,31 +214,30 @@ 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<UserDto>), 200)] [ProducesResponseType(typeof(PageResponse<UserTinyDto>), 200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<ActionResult<PageResponse<UserDto>>> GetFriends(int id, [FromQuery] PageRequest request) public async Task<ActionResult<PageResponse<UserTinyDto>>> 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 athlete = await _userService.GetItemById(id); var totalCount = await _userService.GetNbFriends(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(athlete, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); var friends = await _userService.GetFriends(id, 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<UserDto>(request.Index, request.Count, totalCount, friends.Select(a => a.ToDto())); var pageResponse = new PageResponse<UserTinyDto>(request.Index, request.Count, totalCount, friends);
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");
@ -256,24 +258,13 @@ public class UsersController : Controller
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(404)] [ProducesResponseType(404)]
[ProducesResponseType(500)] [ProducesResponseType(500)]
public async Task<IActionResult> AddFriend(int id, int friendId) public async Task<IActionResult> AddFollowing(int id, int friendId)
{ {
try try
{ {
_logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(AddFriend), friendId,id); _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(AddFollowing), friendId,id);
var athlete = await _userService.GetItemById(id);
if (athlete == null) var isAdded = await _userService.AddFollowing(id, friendId);
{
_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);
@ -281,9 +272,14 @@ 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 getting the number of users"); _logger.LogError(e, "Error while attempting to follow a user");
return Problem(); return Problem();
} }
} }
@ -307,19 +303,8 @@ 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);
if (athlete == null) var isRemoved = await _userService.RemoveFollowing(id, friendId);
{
_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);
@ -327,55 +312,19 @@ public class UsersController : Controller
} }
return Ok(); return Ok();
} }
catch (Exception e) catch(FriendShipException e)
{ {
_logger.LogError(e, "Error while getting the number of users"); _logger.LogError(e, "Error while removing a friend to an athlete");
return Problem(); return BadRequest(e.Message);
}
}
/// <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("Athlete with id {id} not found", coachId);
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 getting the number of users"); _logger.LogError(e, "Error while attempting to unfollow a user");
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>
@ -387,7 +336,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<ActivityDto>>> GetActivitiesByUser(int userId, [FromQuery] PageRequest pageRequest) public async Task<ActionResult<PageResponse<ActivityTinyDto>>> GetActivitiesByUser(int userId, [FromQuery] PageRequest pageRequest)
{ {
try try
{ {
@ -403,7 +352,7 @@ public class UsersController : Controller
{ {
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.Select(a => a.ToDto()));
return Ok(pageResponse); return Ok(pageResponse);
} }
catch (Exception e) catch (Exception e)
@ -411,26 +360,7 @@ public class UsersController : Controller
_logger.LogError(e, "Error while getting all activities"); _logger.LogError(e, "Error while getting all activities");
return Problem(); return Problem();
} }
} }*/
/// <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,11 +10,13 @@
</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.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,4 +1,6 @@
using DbContextLib;
using HeartTrackAPI.Utils; using HeartTrackAPI.Utils;
using StubbedContextLib;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -16,4 +18,8 @@ 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,10 +7,6 @@ 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;
} }

@ -0,0 +1,49 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Entities;
using Microsoft.IdentityModel.Tokens;
namespace HeartTrackAPI.Services;
public interface ITokenService
{
string CreateToken(AthleteEntity user);
}
public class TokenService : ITokenService
{
private readonly IConfiguration _config;
private readonly SymmetricSecurityKey _key;
public TokenService(IConfiguration config)
{
_config = config;
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:SigningKey"]));
}
public string CreateToken(AthleteEntity user)
{
var claims = new List<Claim>
{
new (JwtRegisteredClaimNames.Email, user.Email),
new (JwtRegisteredClaimNames.NameId, user.Id.ToString()),
new (JwtRegisteredClaimNames.GivenName, user.UserName)
};
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds,
Issuer = _config["JWT:Issuer"],
Audience = _config["JWT:Audience"]
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}

@ -1,12 +1,13 @@
using System.Reflection; using System.Reflection;
using DbContextLib; using DbContextLib;
using DbContextLib.Identity; using Entities;
using HeartTrackAPI.Utils; using HeartTrackAPI.Services;
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;
@ -23,19 +24,19 @@ 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); AddIdentityServices(services, configuration);
AddApiVersioning(services); AddApiVersioning(services);
services.AddHealthChecks(); services.AddHealthChecks();
} }
private void AddHeartTrackContextServices(IServiceCollection services) private void AddHeartTrackContextServices(IServiceCollection services)
{ {
string? connectionString; string? connectionString;
switch (Environment.GetEnvironmentVariable("TYPE")) switch (Environment.GetEnvironmentVariable("TYPE"))
{ {
@ -49,40 +50,38 @@ 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(connectionString); Console.WriteLine("====================================================");
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);
break; break;
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;
} }
} }
private void AddModelService(IServiceCollection services) private void AddModelService(IServiceCollection services)
{ {
switch (Environment.GetEnvironmentVariable("TYPE")) switch (Environment.GetEnvironmentVariable("TYPE"))
{ {
case "BDD": case "BDD":
services.AddSingleton<IDataManager>(provider => new DbDataManager(provider.GetRequiredService<HeartTrackContext>())); services.AddSingleton<IDataManager>(provider =>
new DbDataManager(provider.GetRequiredService<HeartTrackContext>()));
break; break;
case "STUB": case "STUB-MODEL":
services.AddSingleton<IDataManager, StubData>(); services.AddSingleton<IDataManager, StubData>();
break; break;
default: default:
@ -91,25 +90,80 @@ 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.AddTransient<IActivityManager, ActivityManager>(); services.AddScoped<ITokenService, TokenService>();
} }
private void AddIdentityServices(IServiceCollection services) 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")
{
identityBuilder.AddEntityFrameworkStores<HeartTrackContext>();
}
else
{
identityBuilder.AddEntityFrameworkStores<TrainingStubbedContext>();
}
services.AddAuthentication(options =>
{
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<IdentityUser>() // services.AddIdentityApiEndpoints<AthleteEntity>()
.AddEntityFrameworkStores<AuthDbContext>(); // .AddEntityFrameworkStores<HeartTrackContext>();
// .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;
@ -119,25 +173,25 @@ 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);
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{ {
Description = Description =
"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.ApiKey Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT"
}); });
var scheme = new OpenApiSecurityRequirement var scheme = new OpenApiSecurityRequirement
{ {
@ -156,50 +210,62 @@ public class AppBootstrap(IConfiguration configuration)
new List<string>() new List<string>()
} }
}; };
options.AddSecurityRequirement(scheme); options.AddSecurityRequirement(scheme);
});
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerOptions>();
services.AddSwaggerGen(options =>
{
options.OperationFilter<SwaggerDefaultValues>(); options.OperationFilter<SwaggerDefaultValues>();
}); });
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerOptions>();
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.MapControllers(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
// app.MapIdentityApi<AthleteEntity>();
app.MapControllers();
app.MapHealthChecks("/health"); app.MapHealthChecks("/health");
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (true) if (true)
{ {
var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>(); app.UseSwagger(options =>
app.UseSwagger();
app.UseSwaggerUI();
app.MapSwagger();
app.UseSwaggerUI(options =>
{ {
foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions) options.PreSerializeFilters.Add((swagger, httpReq) =>
{ {
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", if (httpReq.Headers.ContainsKey("X-Forwarded-Host"))
description.GroupName.ToUpperInvariant()); {
} string basePath;
}); switch (Environment.GetEnvironmentVariable("TYPE")) // httpReq.Host.Value
{
case "STUB":
basePath = "containers/HeartDev-heart_stub";
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();
} }
} }
} }

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

@ -9,6 +9,7 @@ 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,
@ -17,7 +18,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,
"ByIsCoach" => Shared.AthleteOrderCriteria.ByIsCoach, "ByRole" => Shared.AthleteOrderCriteria.ByRole,
_ => Shared.AthleteOrderCriteria.None _ => Shared.AthleteOrderCriteria.None
}; };
} }

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

@ -1,13 +1,18 @@
using Dto;
using Dto.Tiny;
using Shared; using Shared;
namespace Model.Repository; namespace Model.Repository;
public interface IActivityRepository public interface IActivityRepository
{ {
public Task<IEnumerable<Activity>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false); public Task<IEnumerable<ActivityTinyDto>?> 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<Activity?> UpdateActivity(int id, Activity activity); public Task<ResponseActivityDto?> AddActivity(NewActivityDto 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);

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

@ -1,14 +1,23 @@
using Shared; using Dto;
using Dto.Tiny;
using Shared;
namespace Model.Repository; namespace Model.Repository;
public interface IUserRepository : IGenericRepository<User> public interface IUserRepository : IGenericRepository<User> // Make it generic
{ {
// [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<bool> AddFriend(User user, User friend); public Task<IEnumerable<UserTinyDto>?> GetUsersTiny(int index, int count, AthleteOrderCriteria? criteria , bool descending = false);
public Task<bool> RemoveFriend(User user, User friend); public Task<bool> AddFollowing(int fromUser, int toUser);
public Task<IEnumerable<User>?> GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, bool descending = false); public Task<bool> RemoveFollowing(int fromUser, int toUser);
public Task<int> GetNbFriends(User user);
// DELETE
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>?> 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<IEnumerable<User>?> GetAllCoaches(int index, int count, AthleteOrderCriteria? criteria, bool descending = false);

@ -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 string Sexe { get; set; } public char 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, string sexe, float taille, float poids, DateTime dateNaissance, Role role) public User( string username, string profilePicture, string nom, string prenom, string email, string motDePasse, char sexe, float taille, float poids, DateTime dateNaissance, Role role)
{ {
Username = username; Username = username;
ProfilePicture = profilePicture; ProfilePicture = profilePicture;

@ -0,0 +1,63 @@
using Model.utils;
namespace Dto.Tiny;
public class ActivityAnalysis
{
public double AverageHeartRate { get; private set; }
public string AverageHeartRateAdvice { get; private set; }
public double Vo2Max { get; private set; }
public string Vo2MaxAdvice { get; private set; }
public double NormalBpm { get; private set; }
public string NormalBpmAdvice { get; private set; }
public double HeartRateVariability { get; private set; }
public string HeartRateVariabilityAdvice { get; private set; }
public string HeartRateZone { get; private set; }
public string HeartRateZoneAdvice { get; private set; }
public double Duration { get; private set; }
public string DurationAdvice { get; private set; }
public double Effort { get; private set; }
public string EffortAdvice { get; private set; }
public static ActivityAnalysis FromActivityData(ResponseActivityDto activity)
{
double dureeActivity = (activity.EndTime - activity.StartTime).TotalMinutes;
var age = DateTime.Today.Year - activity.Athlete.DateOfBirth.Year;
var gender = activity.Athlete.Sexe;
var poids = activity.Athlete.Weight;
var effortFelt = activity.EffortFelt;
var averageHeartRate = activity.Average;
var heartRateVariability = activity.Variability;
var heartRateZones = HeartRateAdvise.CalculateHeartRateZones(age);
var effortScore = HeartRateAdvise.EvaluateEffort(activity);
var (seuilBPM, vo2Max) = HeartRateAdvise.SeuilBPMavance(gender, age, poids, dureeActivity);
string averageHeartRateAdvice = HeartRateAdvise.GenerateAverageHeartRateAdvice(averageHeartRate, seuilBPM);
string vo2MaxAdvice = HeartRateAdvise.GenerateVo2MaxAdvice(vo2Max);
string normalBpmAdvice = HeartRateAdvise.GenerateNormalBpmAdvice(seuilBPM, averageHeartRate);
string hrvAdvice = HeartRateAdvise.GenerateHrvAdvice(heartRateVariability);
HeartRateAdvise.HeartRateZone currentZone = heartRateZones.Find(zone => averageHeartRate >= zone.MinHeartRate && averageHeartRate <= zone.MaxHeartRate);
string heartRateZoneAdvice = HeartRateAdvise.GenerateHeartRateZoneAdvice(currentZone?.Name);
var effortAccuracy = HeartRateAdvise.CompareEffort(effortFelt, (int)effortScore);
var analysis = new ActivityAnalysis
{
AverageHeartRate = averageHeartRate,
AverageHeartRateAdvice = averageHeartRateAdvice,
Vo2Max = vo2Max,
Vo2MaxAdvice = vo2MaxAdvice,
NormalBpm = seuilBPM,
NormalBpmAdvice = normalBpmAdvice,
HeartRateVariability = heartRateVariability,
HeartRateVariabilityAdvice = hrvAdvice,
HeartRateZone = currentZone != null ? currentZone.Name : "N/A",
HeartRateZoneAdvice = heartRateZoneAdvice,
Duration = dureeActivity,
DurationAdvice =HeartRateAdvise.GenerateDurationAdvice(dureeActivity),
Effort = effortScore,
EffortAdvice = HeartRateAdvise.GenerateEffortAdvice(effortAccuracy)
};
return analysis;
}
}

@ -0,0 +1,278 @@
using Dto;
namespace Model.utils;
public class HeartRateAdvise
{
public class HeartRateZone
{
public string Name { get; set; }
public int MinHeartRate { get; set; }
public int MaxHeartRate { get; set; }
}
public string getAdvise(Activity activity)
{
return "You should take a break";
}
public static List<HeartRateZone> CalculateHeartRateZones(int age)
{
int fcm = 220 - age; // Estimation de la FCM
List<HeartRateZone> zones = new List<HeartRateZone>
{
new HeartRateZone { Name = "Très Légère", MinHeartRate = (int)(fcm * 0.50), MaxHeartRate = (int)(fcm * 0.60) },
new HeartRateZone { Name = "Aérobie légère", MinHeartRate = (int)(fcm * 0.60), MaxHeartRate = (int)(fcm * 0.70) },
new HeartRateZone { Name = "Aérobie", MinHeartRate = (int)(fcm * 0.70), MaxHeartRate = (int)(fcm * 0.80) },
new HeartRateZone { Name = "Anaérobie", MinHeartRate = (int)(fcm * 0.80), MaxHeartRate = (int)(fcm * 0.90) },
new HeartRateZone { Name = "Maximum", MinHeartRate = (int)(fcm * 0.90), MaxHeartRate = (int)(fcm * 1.00) }
};
return zones;
}
public static double EvaluateEffort(ResponseActivityDto stats)
{
double score = 0;
score += stats.Average * 0.3; // Exemple de poids
score += stats.HeartRates.Average(speed => speed.Speed ?? 0) * 0.25;
score += stats.HeartRates.Sum(dist => dist.Distance ?? 0) * 0.15;
score += stats.HeartRates.Average(hr => hr.Cadence ?? 0) * 0.1;
score += stats.HeartRates.Average(hr => hr.Power ?? 0) * 0.15;
score += stats.StandardDeviation * 0.05;
return score;
}
public static string CompareEffort(int perceivedEffort, int objectiveEffort)
{
if (perceivedEffort == objectiveEffort) return "Accurate";
if (perceivedEffort > objectiveEffort) return "Overestimated";
if (perceivedEffort < objectiveEffort) return "Underestimated";
return "Unknown";
}
// Faible intensité : 50-60% de la FCM
// Intensité modérée : 60-70% de la FCM
// Haute intensité : 70-85% de la FCM
public static double CalculerVo2Max(char sexe, int age, double poids)
{
if (sexe == 'M')
{
return (poids * 0.198) + (age * -0.193) + 24.489;
}
// sexe == "F"
{
return (poids * 0.201) + (age * -0.217) + 20.453;
}
}
public static (double SeuilBPM, double Vo2Max) SeuilBPMavance(char sexe, int age, double poids, double dureeActivite)
{
double fcm = sexe == 'M' ? 207 - (0.7 * age) : 206 - (0.88 * age);
double vo2Max = CalculerVo2Max(sexe, age, poids);
double zone = dureeActivite <= 30 ? 0.8 : dureeActivite <= 60 ? 0.7 : 0.6;
double seuilBpm = fcm * zone;
return (seuilBpm, vo2Max);
}
public static Dictionary<string, (double Value, string Advice)> GenerateAdvice(ResponseActivityDto activity)
{
int dureeActivity = (activity.EndTime - activity.StartTime).Minutes;
var age = DateTime.Today.Year - activity.Athlete.DateOfBirth.Year;
var gender = activity.Athlete.Sexe;
var poids = activity.Athlete.Weight;
var effortFelt = activity.EffortFelt;
var averageHeartRate = activity.Average;
var heartRateVariability = activity.Variability;
var heartRateZones = CalculateHeartRateZones(age);
var effortScore = EvaluateEffort(activity);
var (seuilBPM, vo2Max) = SeuilBPMavance(gender, age, poids, dureeActivity);
Dictionary<string, (double Value, string Advice)> healthMetrics = new Dictionary<string, (double Value, string Advice)>();
// Conseil pour AverageHeartRate
string averageHeartRateAdvice = averageHeartRate > seuilBPM
? "Votre rythme cardiaque moyen est supérieur au seuil recommandé. Envisagez de réduire l'intensité."
: "Votre rythme cardiaque moyen est dans une bonne plage. Continuez à maintenir votre intensité actuelle.";
// Conseil pour Vo2Max
string vo2MaxAdvice;
if (vo2Max < 30)
vo2MaxAdvice =
"Votre Vo2 max est faible, envisagez d'augmenter progressivement l'intensité de vos entraînements.";
else if (vo2Max < 40)
vo2MaxAdvice = "Votre Vo2 max est dans la moyenne. Continuez de travailler sur votre endurance.";
else vo2MaxAdvice = "Votre Vo2 max est excellente. Vous pourriez bénéficier d'entraînements à haute intensité.";
// Conseil basé sur la comparaison avec NormalBpm
string normalBpmAdvice = seuilBPM > averageHeartRate
? "Votre BPM normal est plus élevé que la moyenne, assurez-vous de surveiller votre intensité d'entraînement."
: "Votre BPM normal est inférieur à la moyenne, ce qui peut indiquer un bon niveau de forme physique.";
// Conseil pour HeartRateVariability
string hrvAdvice = heartRateVariability < 40
? "Votre HRV est basse, ce qui peut indiquer un stress élevé ou une récupération insuffisante. Envisagez d'améliorer votre récupération."
: heartRateVariability < 60
? "Votre HRV est dans une plage moyenne. Continuez de surveiller votre récupération et votre stress."
: "Votre HRV est élevée, indiquant une bonne récupération et une gestion du stress. Continuez vos bonnes pratiques de gestion de la santé.";
HeartRateZone currentZone = heartRateZones.Find(zone =>
averageHeartRate >= zone.MinHeartRate && averageHeartRate <= zone.MaxHeartRate);
string heartRateZoneAdvice;
if (currentZone != null)
{
heartRateZoneAdvice = $"Votre BPM moyen est dans la zone '{currentZone.Name}', qui est idéale pour ";
switch (currentZone.Name)
{
case "Repos":
heartRateZoneAdvice += "favoriser la récupération.";
break;
case "Endurance":
heartRateZoneAdvice += "améliorer l'endurance cardiovasculaire et brûler des graisses.";
break;
case "Aérobie":
heartRateZoneAdvice += "améliorer votre capacité aérobie.";
break;
case "Anaérobie":
heartRateZoneAdvice += "améliorer la performance à haute intensité.";
break;
case "VO2 Max":
heartRateZoneAdvice += "maximiser la performance et la capacité aérobie.";
break;
default:
heartRateZoneAdvice =
"Cette zone est spécifique et peut avoir différents objectifs en fonction de votre plan d'entraînement.";
break;
}
}
else
{
heartRateZoneAdvice =
"Vous n'êtes dans aucune zone spécifique. Assurez-vous que votre BPM moyen est correctement mesuré.";
}
// Comparaison de l'effort objectif avec l'effort perçu
var effortAccuracy = CompareEffort(effortFelt, (int)effortScore);
// Remplissage du dictionnaire
healthMetrics.Add("AverageHeartRate", (averageHeartRate, averageHeartRateAdvice));
healthMetrics.Add("Vo2Max", (vo2Max, vo2MaxAdvice));
healthMetrics.Add("NormalBpm", (seuilBPM, normalBpmAdvice));
healthMetrics.Add("HeartRateVariability", (heartRateVariability, hrvAdvice));
healthMetrics.Add("HeartRateZone", (averageHeartRate, heartRateZoneAdvice));
healthMetrics.Add("Duration",
(dureeActivity,
dureeActivity < 75
? "Vous pourriez augmenter la durée de vos activités pour atteindre les recommandations."
: "Votre durée d'activité est conforme aux recommandations."));
healthMetrics.Add("Effort", (effortScore, GenerateEffortAdvice(effortAccuracy)));
return healthMetrics;
}
public static string GenerateAverageHeartRateAdvice(float averageHeartRate, double seuilBPM)
{
return averageHeartRate > seuilBPM
? "Votre rythme cardiaque moyen est supérieur au seuil recommandé. Envisagez de réduire l'intensité."
: "Votre rythme cardiaque moyen est dans une bonne plage. Continuez à maintenir votre intensité actuelle.";
}
public static string GenerateVo2MaxAdvice(double vo2Max)
{
if (vo2Max < 30)
return "Votre Vo2 max est faible, envisagez d'augmenter progressivement l'intensité de vos entraînements.";
if (vo2Max < 40)
return "Votre Vo2 max est dans la moyenne. Continuez de travailler sur votre endurance.";
return "Votre Vo2 max est excellente. Vous pourriez bénéficier d'entraînements à haute intensité.";
}
public static string GenerateNormalBpmAdvice(double normalBpm,double averageHeartRate)
{
return normalBpm > averageHeartRate
? "Votre BPM normal est plus élevé que la moyenne, ce qui peut indiquer un bon niveau de forme physique."
: "Votre BPM normal est inférieur à la moyenne, assurez-vous de surveiller votre intensité d'entraînement.";
}
public static string GenerateHrvAdvice(double heartRateVariability)
{
return heartRateVariability < 40
? "Votre HRV est basse, ce qui peut indiquer un stress élevé ou une récupération insuffisante. Envisagez d'améliorer votre récupération."
: heartRateVariability < 60
? "Votre HRV est dans une plage moyenne. Continuez de surveiller votre récupération et votre stress."
: "Votre HRV est élevée, indiquant une bonne récupération et une gestion du stress. Continuez vos bonnes pratiques de gestion de la santé.";
}
public static string GenerateHeartRateZoneAdvice(string? heartRateZone)
{
return heartRateZone switch
{
"Repos" => "Favoriser la récupération.",
"Endurance" => "Améliorer l'endurance cardiovasculaire et brûler des graisses.",
"Aérobie" => "Améliorer votre capacité aérobie.",
"Anaérobie" => "Améliorer la performance à haute intensité.",
"VO2 Max" => "Maximiser la performance et la capacité aérobie.",
_ => "Cette zone est spécifique et peut avoir différents objectifs en fonction de votre plan d'entraînement."
};
}
public static string GenerateDurationAdvice(double dureeActivity)
{
return dureeActivity < 75
? "Vous pourriez augmenter la durée de vos activités pour atteindre les recommandations."
: "Votre durée d'activité est conforme aux recommandations.";
}
public static string GenerateEffortAdvice(string effortAccuracy)
{
return effortAccuracy switch
{
"Accurate" => "Votre perception de l'effort est précise. Continuez ainsi!",
"Overestimated" => "Vous surestimez votre effort. Essayez de pousser un peu plus lors de vos prochaines activités.",
"Underestimated" => "Vous sous-estimez votre effort. Vous pourriez être plus proche de vos limites que vous ne le pensez.",
_ => "Continuez à surveiller et ajuster votre effort pour de meilleurs résultats."
};
}
}
/*
public class EffortAnalysisResult
{
public string EffortAccuracy { get; set; }
public string Advice { get; set; }
}
public EffortAnalysisResult AnalyzeActivityEffort(ResponseActivityDto activity)
{
var effortScore = EvaluateEffort(activity);
// Comparaison de l'effort objectif avec l'effort perçu
var effortAccuracy = CompareEffort(activity.EffortFelt, (int)effortScore);
var result = new EffortAnalysisResult
{
EffortAccuracy = effortAccuracy,
Advice = GenerateEffortAdvice(effortAccuracy)
};
return result;
}
public List<HeartRateTinyDto> GetMockHeartRateData()
{
var random = new Random();
return Enumerable.Range(1, 3600)
.Select(_ => new HeartRateTinyDto
{
HeartRate = random.Next(60, 220),
Timestamp = new DateTime(2021, 1, 1).AddSeconds(random.Next(3600)),
Latitude = random.NextDouble() * 180 - 90,
Longitude = random.NextDouble() * 360 - 180,
Altitude = random.NextDouble() * 1000,
Cadence = random.Next(60, 120),
Distance = random.NextDouble() * 100,
Speed = random.NextDouble() * 30,
Power = random.Next(0, 500),
Temperature = random.NextDouble() * 30
}).ToList();
}
*/

@ -1,3 +1,5 @@
using Dto;
using Dto.Tiny;
using Model; using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
@ -5,6 +7,7 @@ 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;
@ -21,13 +24,13 @@ public partial class DbDataManager : IDataManager
this._logger = logger; this._logger = logger;
} }
public async Task<IEnumerable<Activity>> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) public async Task<IEnumerable<ActivityTinyDto>?> 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
.IncludeStandardProperties().GetItemsWithFilterAndOrdering(b => true, index, count, criteria, descending).ToModels(); .GetItemsWithFilterAndOrdering(b => true, index, count, criteria, descending).ToTinyDtos();
_logger.LogInformation($"Retrieved {activities.Count()} activities"); _logger.LogInformation($"Retrieved {activities.Count()} activities");
return await Task.FromResult(activities); return await Task.FromResult(activities);
@ -47,6 +50,20 @@ public partial class DbDataManager : IDataManager
return await Task.FromResult(activity); 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)
_logger.LogInformation($"Retrieved activity with ID {id}");
else
_logger.LogWarning($"No activity found with ID {id}");
return await Task.FromResult(activity);
}
public async Task<Activity?> AddActivity(Activity activity) public async Task<Activity?> AddActivity(Activity activity)
{ {
@ -67,18 +84,46 @@ public partial class DbDataManager : IDataManager
} }
} }
public async Task<Activity?> UpdateActivity(int id, Activity activity) public async Task<ResponseActivityDto?> AddActivity(NewActivityDto activity)
{
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 try
{ {
_logger.LogInformation($"Updating activity with ID {id}"); var entity =await _dataManager.DbContext.ActivitiesSet.IncludeAll(_dataManager.DbContext)
var updatedActivity = await _dataManager.DbContext.UpdateItem<Activity, ActivityEntity>(id, activity, (activity, entity) => .FirstOrDefaultAsync(a => a.IdActivity == id);
if (entity != null)
{ {
_logger.LogInformation($"Updating activity with ID {id}");
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.Effort; entity.EffortFelt = activity.EffortFelt;
entity.Variability = activity.Variability; entity.Variability = activity.Variability;
entity.Variance = activity.Variance; entity.Variance = activity.Variance;
entity.StandardDeviation = activity.StandardDeviation; entity.StandardDeviation = activity.StandardDeviation;
@ -87,16 +132,15 @@ 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(updatedActivity.ToModel()); return await Task.FromResult(entity.ToResponseDto());
} }
else else
{ {
_logger.LogError($"Failed to update activity with ID {id}"); _logger.LogError($"Failed to update activity with ID {id}");
return await Task.FromResult<Activity?>(null); return await Task.FromResult<ResponseActivityDto?>(null);
} }
} }
catch (Exception ex) catch (Exception ex)

@ -0,0 +1,34 @@
using Dto.Tiny;
using Entities2Dto;
using Microsoft.Extensions.Logging;
using Model.Repository;
namespace Model2Entities;
public partial class DbDataManager
{
public class DataSourceRepository : IDataSourceRepository<DataSourceTinyDto>
{
private readonly DbDataManager _dataManager;
private readonly ILogger<DbDataManager> _logger;
public DataSourceRepository(DbDataManager dbDataManager, ILogger<DbDataManager> logger)
{
_dataManager = dbDataManager;
_logger = logger;
}
public async Task<DataSourceTinyDto?> GetItemById(int id)
{
var dataSource = await _dataManager.DbContext.DataSourcesSet.FindAsync(id);
if (dataSource == null)
{
_logger.LogInformation($"DataSource with ID {id} not found.");
return null;
}
return dataSource.ToTinyDto();
}
}
}

@ -1,4 +1,5 @@
using DbContextLib; using DbContextLib;
using Dto.Tiny;
using EFMappers; using EFMappers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -11,6 +12,8 @@ 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()); protected readonly ILogger<DbDataManager> _logger = new Logger<DbDataManager>(new LoggerFactory());
@ -21,7 +24,9 @@ public partial class DbDataManager: IDataManager
DbContext = dbContext; DbContext = dbContext;
ActivityRepo = new ActivityRepository(this, _logger); ActivityRepo = new ActivityRepository(this, _logger);
UserRepo = new UserRepository(this, _logger); UserRepo = new UserRepository(this, _logger);
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
} }
@ -34,5 +39,6 @@ public partial class DbDataManager: IDataManager
DbContext = new HeartTrackContext(); DbContext = new HeartTrackContext();
ActivityRepo = new ActivityRepository(this, _logger); ActivityRepo = new ActivityRepository(this, _logger);
UserRepo= new UserRepository(this, _logger); UserRepo= new UserRepository(this, _logger);
DataSourceRepo = new DataSourceRepository(this, _logger);
} }
} }

@ -40,13 +40,10 @@ 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;
} }
@ -84,7 +81,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 HeartTrackContext dbContext, IQueryable<TEntity> query) where TEntity : class public static IQueryable<TEntity> IncludeAll<TEntity>(this IQueryable<TEntity> query, HeartTrackContext dbContext) 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())

@ -8,6 +8,7 @@
<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" />

@ -1,9 +1,12 @@
using Dto;
using Dto.Tiny;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Model; using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
using EFMappers; using EFMappers;
using Entities; using Entities;
using Entities2Dto;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Model2Entities; namespace Model2Entities;
@ -39,12 +42,22 @@ public partial class DbDataManager
} }
public async Task<IEnumerable<UserTinyDto>?> GetUsersTiny(int index, int count, AthleteOrderCriteria? criteria, bool descending = false)
{
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)
{ {
_logger.LogInformation($"GetItemById with id {id}", id); _logger.LogInformation($"GetItemById with id {id}", id);
var userEntity = await _dataManager.DbContext.AthletesSet.IncludeStandardProperties() var userEntity = await _dataManager.DbContext.AthletesSet.IncludeStandardProperties()
.SingleOrDefaultAsync(a => a.IdAthlete == id); .SingleOrDefaultAsync(a => a.Id == id);
var user = userEntity != null ? userEntity.ToModel() : null; var user = userEntity != null ? userEntity.ToModel() : null;
if (user != null) if (user != null)
_logger.LogInformation($"Retrieved user with ID {id}"); _logger.LogInformation($"Retrieved user with ID {id}");
@ -54,6 +67,55 @@ public partial class DbDataManager
} }
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); _logger.LogInformation($"UpdateItem with id {oldItem}", oldItem);
@ -107,7 +169,6 @@ public partial class DbDataManager
public async Task<int> GetNbItems() public async Task<int> GetNbItems()
{ {
_logger.LogInformation("GetNbItems"); _logger.LogInformation("GetNbItems");
var nbItems = await _dataManager.DbContext.AthletesSet.CountAsync(); var nbItems = await _dataManager.DbContext.AthletesSet.CountAsync();
_logger.LogInformation($"Retrieved {nbItems} users"); _logger.LogInformation($"Retrieved {nbItems} users");
@ -142,85 +203,136 @@ public partial class DbDataManager
return await Task.FromResult(coaches); return await Task.FromResult(coaches);
} }
public async Task<bool> AddFriend(User user, User friend) public async Task<bool> AddFollowing(int fromUser, int toUser)
{ {
_logger.LogInformation($"Attempting to add friend: User {user.Id} adding Friend {friend.Id}"); _logger.LogInformation($"Attempting to add following: User {fromUser} adding Following {toUser}");
var userEntity = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().FirstOrDefault(a => a.IdAthlete == user.Id);
var friendEntity = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().FirstOrDefault(a => a.IdAthlete == friend.Id); var userEntity = _dataManager.DbContext.AthletesSet
if (userEntity == null || friendEntity == null) .Include(a => a.Followings)
.FirstOrDefault(a => a.Id == fromUser);
if (userEntity == null)
{ {
_logger.LogWarning($"User or friend not found: User {user.Id}, Friend {friend.Id}"); _logger.LogWarning($"User not found: User {fromUser}");
return false; throw new FriendShipException("User with id " + fromUser + " not found");
} }
if (userEntity.Followings.All(f => f.FollowingId != friend.Id)) if (userEntity.Followings.Any(f => f.FollowingId == toUser))
{ {
userEntity.Followings.Add(new FriendshipEntity _logger.LogInformation($"Following already exists: User {fromUser} and Following {toUser}");
{ FollowingId = friend.Id, FollowerId = user.Id, StartDate = DateTime.Now }); throw new FriendShipException("Following already exists");
await _dataManager.DbContext.SaveChangesAsync();
_logger.LogInformation($"Successfully added friend: User {user.Id} added Friend {friend.Id}");
return true;
} }
await _dataManager.DbContext.SaveChangesAsync();
userEntity.Followings.Add(new FriendshipEntity
{
FollowingId = toUser,
FollowerId = fromUser,
StartDate = DateTime.Now
});
_logger.LogInformation($"Friendship already exists: User {user.Id} and Friend {friend.Id}"); await _dataManager.DbContext.SaveChangesAsync();
return false;
_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}");
public async Task<bool> RemoveFriend(User user, User friend) var userEntity = _dataManager.DbContext.AthletesSet
{ .Include(a => a.Followings)
_logger.LogInformation($"Attempting to remove friend: User {user.Id} removing Friend {friend.Id}"); .FirstOrDefault(a => a.Id == fromUser);
var userEntity = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().FirstOrDefault(a => a.IdAthlete == user.Id);
var friendEntity = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().FirstOrDefault(a => a.IdAthlete == friend.Id); if (userEntity == null)
if (userEntity == null || friendEntity == null)
{ {
_logger.LogWarning($"User or friend not found: User {user.Id}, Friend {friend.Id}"); _logger.LogWarning($"User not found: User {fromUser}");
return false; throw new FriendShipException("User with id " + fromUser + " not found");
} }
var friendship = userEntity.Followings.FirstOrDefault(f => f.FollowingId == friend.Id); var friendship = userEntity.Followings.FirstOrDefault(f => f.FollowingId == toUser);
if (friendship != null) if (friendship == null)
{ {
userEntity.Followings.Remove(friendship); _logger.LogInformation($"Following not found: User {fromUser} and Following {toUser}");
await _dataManager.DbContext.SaveChangesAsync(); throw new FriendShipException("Following not found");
_logger.LogInformation($"Successfully removed friend: User {user.Id} removed Friend {friend.Id}");
return true;
} }
await _dataManager.DbContext.SaveChangesAsync();
_logger.LogInformation($"Friendship does not exist: User {user.Id} and Friend {friend.Id}"); userEntity.Followings.Remove(friendship);
return false;
}
public Task<IEnumerable<User>> GetFriends(User user, int index, int count, AthleteOrderCriteria? criteria, 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) bool descending = false)
{ {
try try
{ {
_logger.LogInformation($"GetFriends with index {index} and count {count}", index, count); _logger.LogInformation($"GetFriends called with index {index}, count {count}, criteria {criteria}, and descending {descending}");
_logger.LogInformation($"GetFriends with criteria {criteria} and descending {descending}", criteria,
descending); var athlete = await _dataManager.DbContext.AthletesSet
var friends = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().Include(a => a.Followers).Include(a => a.Followings) .Include(a => a.Followers).ThenInclude(f => f.Follower)
.GetItemsWithFilterAndOrdering(a => a.Followers.Any(f => f.FollowingId == user.Id), index, count, .Include(a => a.Followings).ThenInclude(f => f.Following)
criteria, descending).ToModels(); .FirstOrDefaultAsync(a => a.Id == userId); // Use async version for better performance
_logger.LogInformation($"Retrieved {friends.Count()} friends");
return Task.FromResult(friends); if (athlete == null)
{
_logger.LogError("Athlete with id {id} not found", userId);
throw new ModelNotFoundException($"Athlete with id {userId} not found");
}
var friendsDtos = athlete.Followings
.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) catch (Exception ex)
{ {
_logger.LogError(ex.Message, ex.InnerException, ex.StackTrace); _logger.LogError(ex.Message, ex.InnerException, ex.StackTrace);
return Task.FromResult<IEnumerable<User>>(new List<User>()); return null;
} }
} }
public Task<int> GetNbFriends(User user) public async Task<int> GetNbFriends(int userId)
{ {
try
_logger.LogInformation($"GetNbFriends with user {user}", user); {
var nbFriends = _dataManager.DbContext.AthletesSet _logger.LogInformation($"GetNbFriends called for user {userId}");
.GetItemsWithFilterAndOrdering(a => a.IdAthlete == user.Id, 0, int.MaxValue,
AthleteOrderCriteria.None, false).First().Followings.Count(); var athlete = await _dataManager.DbContext.AthletesSet
_logger.LogInformation($"Retrieved {nbFriends} friends"); .Include(a => a.Followers).ThenInclude(f => f.Follower)
return Task.FromResult(nbFriends); .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
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; // Consider handling the exception outside of this method or logging it accordingly.
}
} }
} }
} }

@ -3,43 +3,16 @@
public enum AthleteOrderCriteria public enum AthleteOrderCriteria
{ {
None, None,
ById,
ByUsername, ByUsername,
ByFirstName, ByFirstName,
ByLastName, ByLastName,
ByEmail,
BySexe, BySexe,
ByLenght, ByLenght,
ByWeight, ByWeight,
ByDateOfBirth, ByDateOfBirth,
ByEmail, ByRole
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) 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,bool useMapper = true) 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);
mapper.Add(t, u); if(useMapper) 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) 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,bool useMapper = true) 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);
mapper.Add(t, u); if(useMapper) mapper.Add(t, u);
if(action != null) action(u, t); if(action != null) action(u, t);
return t; return t;

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

@ -1,3 +1,5 @@
using Dto;
using Dto.Tiny;
using Model; using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
@ -6,17 +8,17 @@ namespace StubAPI;
public class ActivityService: IActivityRepository public class ActivityService: IActivityRepository
{ {
private List<Activity> _activities = new List<Activity>( private List<ActivityTinyDto> _activities = new List<ActivityTinyDto>(
new Activity[] new ActivityTinyDto[]
{ {
new Activity new ActivityTinyDto
{ {
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),
Effort = 3, EffortFelt = 3,
Variability = 0.5f, Variability = 0.5f,
Variance = 0.5f, Variance = 0.5f,
StandardDeviation = 0.5f, StandardDeviation = 0.5f,
@ -25,33 +27,46 @@ 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<Activity>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false) public async Task<IEnumerable<ActivityTinyDto>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false)
=> await Task.FromResult(_activities.GetItemsWithFilterAndOrdering(c=>true,index, count,criteria != ActivityOrderCriteria.None ? criteria: null , descending)); => await Task.FromResult(_activities.GetItemsWithFilterAndOrdering(a => true, index, count, criteria, descending));
public Task<Activity?> GetActivityByIdAsync(int id) public Task<Activity?> GetActivityByIdAsync(int id)
{ {
return Task.FromResult(_activities.FirstOrDefault(s => s.Id == id)); throw new NotImplementedException();
// 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)
=> _activities.AddItem(activity); => throw new NotImplementedException();
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)
{ {
var oldActivity = _activities.FirstOrDefault(s => s.Id == id); throw new NotImplementedException();
/*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)
@ -66,13 +81,16 @@ 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)
{ {
var activities = _activities.GetItemsWithFilterAndOrdering(a => a.Athlete.Id == userId, index, count, throw new NotImplementedException();
/* 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)
{ {
return Task.FromResult(_activities.Count(a => a.Athlete.Id == userId)); throw new NotImplementedException();
// return Task.FromResult(_activities.Count(a => a.Athlete.Id == userId));
} }
} }

@ -1,4 +1,6 @@
using Model; using Dto;
using Dto.Tiny;
using Model;
using Model.Repository; using Model.Repository;
using Shared; using Shared;
@ -11,21 +13,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()
} }
@ -34,6 +36,26 @@ 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)
@ -71,9 +93,24 @@ 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(User user) public Task<int> GetNbFriends(int user)
{
return Task.FromResult(athletes.FirstOrDefault(s => s.Id == user)?.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)
{ {
return Task.FromResult(athletes.FirstOrDefault(s => s.Id == user.Id)?.Users.Count ?? 0); 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,

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

@ -1,3 +1,4 @@
using Dto.Tiny;
using Model.Manager; using Model.Manager;
using Model.Repository; using Model.Repository;
@ -7,6 +8,7 @@ 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()
{ {

@ -1,4 +0,0 @@
// 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 { 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 } 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 }
); );
} }
} }

@ -39,10 +39,7 @@ 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 = 1, FollowingId = 4 }, new FriendshipEntity { FollowerId = 3, FollowingId = 1 }
new FriendshipEntity { FollowerId = 1, FollowingId = 5 },
new FriendshipEntity { FollowerId = 2, FollowingId = 1 },
new FriendshipEntity { FollowerId = 2, FollowingId = 3 }
); );
} }
} }

@ -8,20 +8,19 @@ using Shared;
using StubbedContextLib; using StubbedContextLib;
using static Model2Entities.DbDataManager; using static Model2Entities.DbDataManager;
namespace Model2Entities namespace ConsoleTestEFMapper;
{
static 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
var dataManager = new DbDataManager(new TrainingStubbedContext()); Console.WriteLine("");
var logger = new Logger<DbDataManager>(new LoggerFactory());
// Test de la méthode GetActivities
await ActivitiesTestAsync(dataManager, logger);
} }
/*
static async Task ActivitiesTestAsync(DbDataManager dataManager, ILogger<DbDataManager> logger) static async Task ActivitiesTestAsync(DbDataManager dataManager, ILogger<DbDataManager> logger)
{ {
var activityRepository = new ActivityRepository(dataManager, logger); var activityRepository = new ActivityRepository(dataManager, logger);
@ -48,7 +47,7 @@ namespace Model2Entities
} }
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
{ {
@ -113,6 +112,5 @@ namespace Model2Entities
Console.WriteLine($"Total number of activities: {itemCount}"); Console.WriteLine($"Total number of activities: {itemCount}");
} }
static void UsersTest() 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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete == 2)) foreach (var athlete in db.AthletesSet.Where(a => a.Id == 2))
{ {
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($"\t{athlete.Id} - {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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); 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("---------------------------------\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", Password = "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", PasswordHash = "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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.Id} - {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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.Id} - {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.IdAthlete} - {athlete.Username}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.Id} - {athlete.UserName}, {athlete.LastName}, {athlete.FirstName}, {athlete.Email}, {athlete.Sexe}, {athlete.Length}, {athlete.Weight}, {athlete.DateOfBirth}, {athlete.IsCoach}");
} }
} }

@ -96,7 +96,7 @@ class Program
foreach (var athlete in dataSource.Athletes) foreach (var athlete in dataSource.Athletes)
{ {
Console.WriteLine($"\t\t\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t\t\t{athlete.Id} - {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.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t\t\t{athlete.Id} - {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.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.Id} - {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.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)) 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))
{ {
Console.WriteLine($"\t{athlete.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.Id} - {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.IdAthlete} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}"); Console.WriteLine($"\t{athlete.Id} - {athlete.FirstName}, {athlete.LastName}, {athlete.DateOfBirth}, {athlete.Sexe}, {athlete.Weight}, {athlete.IsCoach}");
Console.WriteLine($""); Console.WriteLine($"");
Console.WriteLine($""); Console.WriteLine($"");

@ -0,0 +1,114 @@
using Xunit;
using Model2Entities;
using Microsoft.EntityFrameworkCore;
using DbContextLib;
using StubbedContextLib;
using System.Linq;
using Microsoft.Data.Sqlite;
using System;
using EFMappers;
using Shared;
using Model;
using Moq;
using Microsoft.Extensions.Logging;
using Entities;
/*
namespace UnitTestsEntities
{
public class ActivityRepositoryTests : IClassFixture<DatabaseFixture>
{
private readonly DatabaseFixture _fixture;
public ActivityRepositoryTests(DatabaseFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task GetActivities_ReturnsActivities()
{
var options = new DbContextOptionsBuilder<HeartTrackContext>()
.UseSqlite(_fixture._connection)
.Options;
using (var context = new HeartTrackContext(options))
{
context.Database.EnsureCreated();
}
using (var context = new HeartTrackContext(options))
{
var repository = new DbDataManager.ActivityRepository(new DbDataManager(context), null);
var activities = await repository.GetActivities(0, 10, ActivityOrderCriteria.None);
Assert.NotNull(activities);
Assert.Equal(10, activities.Count());
}
}
[Fact]
public async Task GetActivityByIdAsync_ReturnsCorrectActivity_WhenIdExists()
{
// Arrange
var activityId = 1;
var expectedActivity = new Activity { Id = activityId, Type = "Running" };
var mockDataManager = new Mock<DbDataManager>();
mockDataManager.Setup(dm => dm.DbContext.ActivitiesSet.SingleOrDefaultAsync(a => a.IdActivity == activityId))
.ReturnsAsync(expectedActivity.ToEntity());
var loggerMock = new Mock<ILogger<DbDataManager>>();
var activityRepository = new DbDataManager.ActivityRepository(mockDataManager.Object, loggerMock.Object);
// Act
var result = await activityRepository.GetActivityByIdAsync(activityId);
// Assert
Assert.NotNull(result);
Assert.Equal(expectedActivity.Id, result.Id);
Assert.Equal(expectedActivity.Type, result.Type);
}
[Fact]
public async Task GetActivityByIdAsync_ReturnsNull_WhenIdDoesNotExist()
{
// Arrange
var activityId = 999;
var mockDataManager = new Mock<DbDataManager>();
mockDataManager.Setup(dm => dm.DbContext.ActivitiesSet.SingleOrDefaultAsync(a => a.IdActivity == activityId))
.ReturnsAsync((ActivityEntity)null);
var loggerMock = new Mock<ILogger<DbDataManager>>();
var activityRepository = new DbDataManager.ActivityRepository(mockDataManager.Object, loggerMock.Object);
// Act
var result = await activityRepository.GetActivityByIdAsync(activityId);
// Assert
Assert.Null(result);
}
[Fact]
public async Task AddActivity_SuccessfullyAddsNewActivity()
{
// Arrange
var newActivity = new Activity { Type = "Walking" };
var mockDataManager = new Mock<DbDataManager>();
mockDataManager.Setup(dm => dm.DbContext.AddItem(It.IsAny<ActivityEntity>()))
.ReturnsAsync(newActivity.ToEntity());
var loggerMock = new Mock<ILogger<DbDataManager>>();
var activityRepository = new DbDataManager.ActivityRepository(mockDataManager.Object, loggerMock.Object);
// Act
var result = await activityRepository.AddActivity(newActivity);
// Assert
Assert.NotNull(result);
Assert.Equal(newActivity.Type, result.Type);
}
}
}
*/

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Model2Entities\Model2Entities.csproj" />
<ProjectReference Include="..\UnitTestsEntities\UnitTestsEntities.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,10 @@
namespace RepositoriesUnitTest;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}

@ -1 +0,0 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4"/>
<PackageReference Include="MSTest.TestFramework" Version="3.0.4"/>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\HeartTrackAPI\HeartTrackAPI.csproj" />
<ProjectReference Include="..\..\Model\Model.csproj" />
<ProjectReference Include="..\..\Shared\Shared.csproj" />
<ProjectReference Include="..\..\StubAPI\StubAPI.csproj" />
</ItemGroup>
</Project>

@ -1,2 +1,2 @@
// See https://aka.ms/new-console-template for more information // See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!"); Console.WriteLine("Hello, World!");

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

@ -1,10 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

@ -1,12 +0,0 @@
namespace ClientTests;
public class HttpClientManager
{
protected readonly HttpClient _httpClient;
public HttpClientManager(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://localhost:7252");
}
}

@ -1,2 +0,0 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

@ -1,5 +1,6 @@
using APIMappers; using APIMappers;
using Dto; using Dto;
using Dto.Tiny;
using HeartTrackAPI.Controllers; using HeartTrackAPI.Controllers;
using HeartTrackAPI.Request; using HeartTrackAPI.Request;
using HeartTrackAPI.Responce; using HeartTrackAPI.Responce;
@ -31,7 +32,7 @@ public class UsersControllerTest
ProfilePicture = 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", "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", 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()
}, },
@ -41,7 +42,7 @@ public class UsersControllerTest
ProfilePicture = 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", "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", 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()
}, },
@ -51,7 +52,7 @@ public class UsersControllerTest
ProfilePicture = 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", "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", 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()
} }
]; ];
@ -75,14 +76,6 @@ public class UsersControllerTest
_usersController = new UsersController(new NullLogger<UsersController>(), _dataManagerMock.Object); _usersController = new UsersController(new NullLogger<UsersController>(), _dataManagerMock.Object);
} }
/* /*
[TestInitialize]
public void SetUp()
{
_dataManager = new StubData();
_usersController = new UsersController(new NullLogger<UsersController>(), _dataManager);
}*/
[TestMethod] [TestMethod]
public async Task Get_ReturnsPageResponse_WhenRequestIsValid() public async Task Get_ReturnsPageResponse_WhenRequestIsValid()
{ {
@ -101,9 +94,9 @@ public class UsersControllerTest
var okResult = result.Result as OkObjectResult; var okResult = result.Result as OkObjectResult;
// Assert // Assert
Assert.IsNotNull(okResult); Assert.IsNotNull(okResult);
Assert.IsInstanceOfType(okResult.Value, typeof(PageResponse<UserDto>)); Assert.IsInstanceOfType(okResult.Value, typeof(PageResponse<UserTinyDto>));
var pageResponse = okResult.Value as PageResponse<UserDto>; var pageResponse = okResult.Value as PageResponse<UserTinyDto>;
Assert.IsNotNull(pageResponse); Assert.IsNotNull(pageResponse);
Assert.AreEqual(3, pageResponse.Items.Count()); Assert.AreEqual(3, pageResponse.Items.Count());
Assert.AreEqual(3, pageResponse.Total); Assert.AreEqual(3, pageResponse.Total);
Assert.AreEqual(0, pageResponse.Index); Assert.AreEqual(0, pageResponse.Index);
@ -132,8 +125,8 @@ public class UsersControllerTest
var okResult = result.Result as OkObjectResult; var okResult = result.Result as OkObjectResult;
// Assert // Assert
Assert.IsNotNull(okResult); Assert.IsNotNull(okResult);
Assert.IsInstanceOfType(okResult.Value, typeof(PageResponse<UserDto>)); Assert.IsInstanceOfType(okResult.Value, typeof(PageResponse<UserTinyDto>));
var pageResponse = okResult.Value as PageResponse<UserDto>; var pageResponse = okResult.Value as PageResponse<UserTinyDto>;
Assert.IsNotNull(pageResponse); Assert.IsNotNull(pageResponse);
Assert.AreEqual(expectedItemCount, pageResponse.Items.Count()); Assert.AreEqual(expectedItemCount, pageResponse.Items.Count());
} }
@ -162,7 +155,10 @@ public class UsersControllerTest
{ {
var id = 1; var id = 1;
_dataManagerMock.Setup(dm => dm.UserRepo.GetItemById(id)).ReturnsAsync(_users.First(x => x.Id == id)); _dataManagerMock.Setup(dm => dm.UserRepo.GetUserById(id)).ReturnsAsync(List<ResponseUserDto>()
{
});
// Act // Act
var result = await _usersController.GetById(id) ; var result = await _usersController.GetById(id) ;
@ -173,12 +169,12 @@ public class UsersControllerTest
Assert.IsNotNull(okResult); Assert.IsNotNull(okResult);
var resultObject = result.Result as OkObjectResult; var resultObject = result.Result as OkObjectResult;
Assert.IsNotNull(resultObject); Assert.IsNotNull(resultObject);
Assert.IsInstanceOfType(resultObject.Value, typeof(UserDto)); Assert.IsInstanceOfType(resultObject.Value, typeof(ResponseUserDto));
var user = resultObject.Value as UserDto; var user = resultObject.Value as ResponseUserDto;
Assert.IsNotNull(user); Assert.IsNotNull(user);
var tmp = _users.First(x => x.Id == id).ToDto(); var tmp = _users.First(x => x.Id == id).ToDto();
Assert.AreEqual(tmp.Id, user.Id); Assert.AreEqual(tmp.Id, user.Id);
} }*/
[TestMethod] [TestMethod]
public async Task GetById_ReturnsUserDto_WhenRequestUserDoesNotExist() public async Task GetById_ReturnsUserDto_WhenRequestUserDoesNotExist()

@ -12,14 +12,14 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "john_doe", UserName = "john_doe",
LastName = "Doe", LastName = "Doe",
FirstName = "John", FirstName = "John",
Email = "john.doe@example.com", Email = "john.doe@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.5f, Weight = 75.5f,
Password = "password", PasswordHash = "password",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
ProfilPicture = "profile.jpg", ProfilPicture = "profile.jpg",
@ -36,9 +36,9 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var savedAthlete = context.AthletesSet.First(a => a.Username == "john_doe"); var savedAthlete = context.AthletesSet.First(a => a.UserName == "john_doe");
Assert.NotNull(savedAthlete); Assert.NotNull(savedAthlete);
Assert.Equal("john_doe", savedAthlete.Username); Assert.Equal("john_doe", savedAthlete.UserName);
} }
} }
@ -47,14 +47,14 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "jane_smith", UserName = "jane_smith",
LastName = "Smith", LastName = "Smith",
FirstName = "Jane", FirstName = "Jane",
Email = "jane.smith@example.com", Email = "jane.smith@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "password123", PasswordHash = "password123",
DateOfBirth = new DateOnly(1995, 5, 10), DateOfBirth = new DateOnly(1995, 5, 10),
IsCoach = false, IsCoach = false,
DataSourceId = 2, DataSourceId = 2,
@ -70,16 +70,16 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var savedAthlete = context.AthletesSet.First(a => a.Username == "jane_smith"); var savedAthlete = context.AthletesSet.First(a => a.UserName == "jane_smith");
savedAthlete.Username = "jane_doe"; savedAthlete.UserName = "jane_doe";
context.SaveChanges(); context.SaveChanges();
} }
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var updatedAthlete = context.AthletesSet.First(a => a.Username == "jane_doe"); var updatedAthlete = context.AthletesSet.First(a => a.UserName == "jane_doe");
Assert.NotNull(updatedAthlete); Assert.NotNull(updatedAthlete);
Assert.Equal("jane_doe", updatedAthlete.Username); Assert.Equal("jane_doe", updatedAthlete.UserName);
Assert.Equal("Smith", updatedAthlete.LastName); Assert.Equal("Smith", updatedAthlete.LastName);
} }
} }
@ -89,14 +89,14 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "test_user", UserName = "test_user",
LastName = "Test", LastName = "Test",
FirstName = "User", FirstName = "User",
Email = "test.user@example.com", Email = "test.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "testpassword", PasswordHash = "testpassword",
DateOfBirth = new DateOnly(1985, 10, 20), DateOfBirth = new DateOnly(1985, 10, 20),
IsCoach = false, IsCoach = false,
DataSourceId = 3 DataSourceId = 3
@ -111,14 +111,14 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var savedAthlete = context.AthletesSet.First(a => a.Username == "test_user"); var savedAthlete = context.AthletesSet.First(a => a.UserName == "test_user");
context.AthletesSet.Remove(savedAthlete); context.AthletesSet.Remove(savedAthlete);
context.SaveChanges(); context.SaveChanges();
} }
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var deletedAthlete = context.AthletesSet.FirstOrDefault(a => a.Username == "test_user"); var deletedAthlete = context.AthletesSet.FirstOrDefault(a => a.UserName == "test_user");
Assert.Null(deletedAthlete); Assert.Null(deletedAthlete);
} }
} }
@ -128,14 +128,14 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "john_doe", UserName = "john_doe",
LastName = "Doe", LastName = "Doe",
FirstName = "John", FirstName = "John",
Email = "john.doe@example.com", Email = "john.doe@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.5f, Weight = 75.5f,
Password = "password", PasswordHash = "password",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -150,16 +150,16 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var savedAthlete = context.AthletesSet.First(a => a.Username == "john_doe"); var savedAthlete = context.AthletesSet.First(a => a.UserName == "john_doe");
Assert.NotNull(savedAthlete); Assert.NotNull(savedAthlete);
Assert.Equal("john_doe", savedAthlete.Username); Assert.Equal("john_doe", savedAthlete.UserName);
Assert.Equal("Doe", savedAthlete.LastName); Assert.Equal("Doe", savedAthlete.LastName);
Assert.Equal("John", savedAthlete.FirstName); Assert.Equal("John", savedAthlete.FirstName);
Assert.Equal("john.doe@example.com", savedAthlete.Email); Assert.Equal("john.doe@example.com", savedAthlete.Email);
Assert.Equal("M", savedAthlete.Sexe); Assert.Equal('M', savedAthlete.Sexe);
Assert.Equal(180.0, savedAthlete.Length); Assert.Equal(180.0, savedAthlete.Length);
Assert.Equal(75.5f, savedAthlete.Weight); Assert.Equal(75.5f, savedAthlete.Weight);
Assert.Equal("password", savedAthlete.Password); Assert.Equal("password", savedAthlete.PasswordHash);
Assert.Equal(new DateOnly(1990, 1, 1), savedAthlete.DateOfBirth); Assert.Equal(new DateOnly(1990, 1, 1), savedAthlete.DateOfBirth);
Assert.False(savedAthlete.IsCoach); Assert.False(savedAthlete.IsCoach);
} }
@ -170,14 +170,14 @@ public class AthleteEntityTests (DatabaseFixture fixture) : IClassFixture<Databa
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = null, UserName = null,
LastName = null, LastName = null,
FirstName = null, FirstName = null,
Email = null, Email = null,
Sexe = null, Sexe = 'M',
Length = 0, Length = 0,
Weight = 0, Weight = 0,
Password = null, PasswordHash = null,
DateOfBirth = default, DateOfBirth = default,
IsCoach = false IsCoach = false
}; };

@ -7,7 +7,7 @@ namespace UnitTestsEntities;
public class DatabaseFixture : IDisposable public class DatabaseFixture : IDisposable
{ {
private readonly SqliteConnection _connection; public readonly SqliteConnection _connection;
public readonly DbContextOptions<HeartTrackContext> _options; public readonly DbContextOptions<HeartTrackContext> _options;
public DatabaseFixture() public DatabaseFixture()
{ {

@ -12,14 +12,14 @@ public class FriendshipEntityTests (DatabaseFixture fixture) : IClassFixture<Dat
{ {
var follower = new AthleteEntity var follower = new AthleteEntity
{ {
Username = "follower_user1", UserName = "follower_user1",
LastName = "Follower", LastName = "Follower",
FirstName = "User", FirstName = "User",
Email = "follower.user@example.com", Email = "follower.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "followerpassword", PasswordHash = "followerpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -27,14 +27,14 @@ public class FriendshipEntityTests (DatabaseFixture fixture) : IClassFixture<Dat
var following = new AthleteEntity var following = new AthleteEntity
{ {
Username = "following_user1", UserName = "following_user1",
LastName = "Following", LastName = "Following",
FirstName = "User", FirstName = "User",
Email = "following.user@example.com", Email = "following.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "followingpassword", PasswordHash = "followingpassword",
DateOfBirth = new DateOnly(1995, 5, 10), DateOfBirth = new DateOnly(1995, 5, 10),
IsCoach = false, IsCoach = false,
DataSourceId = 2 DataSourceId = 2
@ -63,13 +63,13 @@ public class FriendshipEntityTests (DatabaseFixture fixture) : IClassFixture<Dat
using (var context = new TrainingStubbedContext(fixture._options)) using (var context = new TrainingStubbedContext(fixture._options))
{ {
var savedFriendship = context.AthletesSet.Include(a => a.Followers).Include(a => a.Followings).FirstOrDefault(a => a.Username == "follower_user1").Followers.FirstOrDefault(f => f.FollowerId == follower.IdAthlete && f.FollowingId == following.IdAthlete); var savedFriendship = context.AthletesSet.Include(a => a.Followers).Include(a => a.Followings).FirstOrDefault(a => a.UserName == "follower_user1").Followings.FirstOrDefault(f => f.FollowerId == follower.Id && f.FollowingId == following.Id);
Assert.NotNull(savedFriendship); Assert.NotNull(savedFriendship);
if (savedFriendship != null) if (savedFriendship != null)
{ {
Assert.Equal(follower.IdAthlete, savedFriendship.FollowerId); Assert.Equal(follower.Id, savedFriendship.FollowerId);
Assert.Equal(following.IdAthlete, savedFriendship.FollowingId); Assert.Equal(following.Id, savedFriendship.FollowingId);
} }
} }
} }
@ -79,42 +79,42 @@ public class FriendshipEntityTests (DatabaseFixture fixture) : IClassFixture<Dat
{ {
var follower = new AthleteEntity var follower = new AthleteEntity
{ {
Username = "follower_user", UserName = "follower_user",
LastName = "Follower", LastName = "Follower",
FirstName = "User", FirstName = "User",
Email = "follower.user@example.com", Email = "follower.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "followerpassword", PasswordHash = "followerpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false IsCoach = false
}; };
var following = new AthleteEntity var following = new AthleteEntity
{ {
Username = "following_user", UserName = "following_user",
LastName = "Following", LastName = "Following",
FirstName = "User", FirstName = "User",
Email = "following.user@example.com", Email = "following.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "followingpassword", PasswordHash = "followingpassword",
DateOfBirth = new DateOnly(1995, 5, 10), DateOfBirth = new DateOnly(1995, 5, 10),
IsCoach = false IsCoach = false
}; };
var thirdAthlete = new AthleteEntity var thirdAthlete = new AthleteEntity
{ {
Username = "third_user", UserName = "third_user",
LastName = "Third", LastName = "Third",
FirstName = "User", FirstName = "User",
Email = "third.user@example.com", Email = "third.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.0f, Weight = 75.0f,
Password = "thirdpassword", PasswordHash = "thirdpassword",
DateOfBirth = new DateOnly(1988, 3, 15), DateOfBirth = new DateOnly(1988, 3, 15),
IsCoach = false IsCoach = false
}; };
@ -157,28 +157,28 @@ public class FriendshipEntityTests (DatabaseFixture fixture) : IClassFixture<Dat
// Act // Act
var follower = new AthleteEntity var follower = new AthleteEntity
{ {
Username = "follower_user", UserName = "follower_user",
LastName = "Follower", LastName = "Follower",
FirstName = "User", FirstName = "User",
Email = "follower.user@example.com", Email = "follower.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "followerpassword", PasswordHash = "followerpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false IsCoach = false
}; };
var following = new AthleteEntity var following = new AthleteEntity
{ {
Username = "following_user", UserName = "following_user",
LastName = "Following", LastName = "Following",
FirstName = "User", FirstName = "User",
Email = "following.user@example.com", Email = "following.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "followingpassword", PasswordHash = "followingpassword",
DateOfBirth = new DateOnly(1995, 5, 10), DateOfBirth = new DateOnly(1995, 5, 10),
IsCoach = false IsCoach = false
}; };

@ -12,14 +12,14 @@ public class NotificationEntityTests (DatabaseFixture fixture) : IClassFixture<D
{ {
var sender = new AthleteEntity var sender = new AthleteEntity
{ {
Username = "sender_user", UserName = "sender_user",
LastName = "Sender", LastName = "Sender",
FirstName = "User", FirstName = "User",
Email = "sender.user@example.com", Email = "sender.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "senderpassword", PasswordHash = "senderpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -55,14 +55,14 @@ public class NotificationEntityTests (DatabaseFixture fixture) : IClassFixture<D
{ {
var sender = new AthleteEntity var sender = new AthleteEntity
{ {
Username = "sender_user", UserName = "sender_user",
LastName = "Sender", LastName = "Sender",
FirstName = "User", FirstName = "User",
Email = "sender.user@example.com", Email = "sender.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "senderpassword", PasswordHash = "senderpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -105,14 +105,14 @@ public class NotificationEntityTests (DatabaseFixture fixture) : IClassFixture<D
{ {
var sender = new AthleteEntity var sender = new AthleteEntity
{ {
Username = "sender_user", UserName = "sender_user",
LastName = "Sender", LastName = "Sender",
FirstName = "User", FirstName = "User",
Email = "sender.user@example.com", Email = "sender.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "senderpassword", PasswordHash = "senderpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 3 DataSourceId = 3
@ -154,14 +154,14 @@ public class NotificationEntityTests (DatabaseFixture fixture) : IClassFixture<D
{ {
var sender = new AthleteEntity var sender = new AthleteEntity
{ {
Username = "sender_user", UserName = "sender_user",
LastName = "Sender", LastName = "Sender",
FirstName = "User", FirstName = "User",
Email = "sender.user@example.com", Email = "sender.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "senderpassword", PasswordHash = "senderpassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 3 DataSourceId = 3

@ -12,14 +12,14 @@ public class StatisticEntityTests (DatabaseFixture fixture) : IClassFixture<Data
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -56,14 +56,14 @@ public class StatisticEntityTests (DatabaseFixture fixture) : IClassFixture<Data
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -107,14 +107,14 @@ public class StatisticEntityTests (DatabaseFixture fixture) : IClassFixture<Data
{ {
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -158,14 +158,14 @@ public class StatisticEntityTests (DatabaseFixture fixture) : IClassFixture<Data
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 170.0, Length = 170.0,
Weight = 70.0f, Weight = 70.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 1, 1), DateOfBirth = new DateOnly(1990, 1, 1),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1

@ -12,14 +12,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
{ {
var coach = new AthleteEntity var coach = new AthleteEntity
{ {
Username = "coach_user", UserName = "coach_user",
LastName = "Coach", LastName = "Coach",
FirstName = "User", FirstName = "User",
Email = "coach.user@example.com", Email = "coach.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.0f, Weight = 75.0f,
Password = "coachpassword", PasswordHash = "coachpassword",
DateOfBirth = new DateOnly(1985, 5, 15), DateOfBirth = new DateOnly(1985, 5, 15),
IsCoach = true, IsCoach = true,
DataSourceId = 1 DataSourceId = 1
@ -27,14 +27,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 3, 20), DateOfBirth = new DateOnly(1990, 3, 20),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -73,14 +73,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
{ {
var coach = new AthleteEntity var coach = new AthleteEntity
{ {
Username = "coach_user", UserName = "coach_user",
LastName = "Coach", LastName = "Coach",
FirstName = "User", FirstName = "User",
Email = "coach.user@example.com", Email = "coach.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.0f, Weight = 75.0f,
Password = "coachpassword", PasswordHash = "coachpassword",
DateOfBirth = new DateOnly(1985, 5, 15), DateOfBirth = new DateOnly(1985, 5, 15),
IsCoach = true, IsCoach = true,
DataSourceId = 1 DataSourceId = 1
@ -88,14 +88,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 3, 20), DateOfBirth = new DateOnly(1990, 3, 20),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -141,14 +141,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
{ {
var coach = new AthleteEntity var coach = new AthleteEntity
{ {
Username = "coach_user", UserName = "coach_user",
LastName = "Coach", LastName = "Coach",
FirstName = "User", FirstName = "User",
Email = "coach.user@example.com", Email = "coach.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.0f, Weight = 75.0f,
Password = "coachpassword", PasswordHash = "coachpassword",
DateOfBirth = new DateOnly(1985, 5, 15), DateOfBirth = new DateOnly(1985, 5, 15),
IsCoach = true, IsCoach = true,
DataSourceId = 1 DataSourceId = 1
@ -156,14 +156,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 3, 20), DateOfBirth = new DateOnly(1990, 3, 20),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1
@ -208,14 +208,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
{ {
var coach = new AthleteEntity var coach = new AthleteEntity
{ {
Username = "coach_user", UserName = "coach_user",
LastName = "Coach", LastName = "Coach",
FirstName = "User", FirstName = "User",
Email = "coach.user@example.com", Email = "coach.user@example.com",
Sexe = "M", Sexe = 'M',
Length = 180.0, Length = 180.0,
Weight = 75.0f, Weight = 75.0f,
Password = "coachpassword", PasswordHash = "coachpassword",
DateOfBirth = new DateOnly(1985, 5, 15), DateOfBirth = new DateOnly(1985, 5, 15),
IsCoach = true, IsCoach = true,
DataSourceId = 1 DataSourceId = 1
@ -223,14 +223,14 @@ public class TrainingEntityTests (DatabaseFixture fixture) : IClassFixture<Datab
var athlete = new AthleteEntity var athlete = new AthleteEntity
{ {
Username = "athlete_user", UserName = "athlete_user",
LastName = "Athlete", LastName = "Athlete",
FirstName = "User", FirstName = "User",
Email = "athlete.user@example.com", Email = "athlete.user@example.com",
Sexe = "F", Sexe = 'F',
Length = 165.0, Length = 165.0,
Weight = 60.0f, Weight = 60.0f,
Password = "athletepassword", PasswordHash = "athletepassword",
DateOfBirth = new DateOnly(1990, 3, 20), DateOfBirth = new DateOnly(1990, 3, 20),
IsCoach = false, IsCoach = false,
DataSourceId = 1 DataSourceId = 1

@ -10,7 +10,7 @@ namespace Model.Tests
{ {
var athlete = new Athlete(); var athlete = new Athlete();
var user = new User("hello13", "dkjd.png", "John", "Doe", "john@exemple.com", "password", "Male", 180.5f, 75.3f, new System.DateTime(1990, 5, 15), new Athlete()); var user = new User("hello13", "dkjd.png", "John", "Doe", "john@exemple.com", "password", 'M', 180.5f, 75.3f, new System.DateTime(1990, 5, 15), new Athlete());
var result = athlete.CheckAdd(user); var result = athlete.CheckAdd(user);
Assert.True(result); Assert.True(result);
@ -21,7 +21,7 @@ namespace Model.Tests
{ {
var athlete = new Athlete(); var athlete = new Athlete();
var user = new User("hello13", "dkjd.png", "John", "Doe", "john@exemple.com", "password", "Male", 180.5f, 75.3f, new System.DateTime(1990, 5, 15), new Coach()); var user = new User("hello13", "dkjd.png", "John", "Doe", "john@exemple.com", "password", 'M', 180.5f, 75.3f, new System.DateTime(1990, 5, 15), new Coach());
var result = athlete.CheckAdd(user); var result = athlete.CheckAdd(user);
Assert.False(result); Assert.False(result);

@ -17,7 +17,7 @@ namespace UnitTestsModel
[InlineData("ByWeight", Shared.AthleteOrderCriteria.ByWeight)] [InlineData("ByWeight", Shared.AthleteOrderCriteria.ByWeight)]
[InlineData("ByDateOfBirth", Shared.AthleteOrderCriteria.ByDateOfBirth)] [InlineData("ByDateOfBirth", Shared.AthleteOrderCriteria.ByDateOfBirth)]
[InlineData("ByEmail", Shared.AthleteOrderCriteria.ByEmail)] [InlineData("ByEmail", Shared.AthleteOrderCriteria.ByEmail)]
[InlineData("ByIsCoach", Shared.AthleteOrderCriteria.ByIsCoach)] [InlineData("ByRole", Shared.AthleteOrderCriteria.ByRole)]
[InlineData(null, Shared.AthleteOrderCriteria.None)] [InlineData(null, Shared.AthleteOrderCriteria.None)]
[InlineData("InvalidValue", Shared.AthleteOrderCriteria.None)] [InlineData("InvalidValue", Shared.AthleteOrderCriteria.None)]
public void ToEnum_WithValidValue_ReturnsCorrectEnumValue(string? value, Shared.AthleteOrderCriteria expected) public void ToEnum_WithValidValue_ReturnsCorrectEnumValue(string? value, Shared.AthleteOrderCriteria expected)

@ -8,7 +8,7 @@ namespace Model.Tests
public void AddUser_WithValidUser_ReturnsTrue() public void AddUser_WithValidUser_ReturnsTrue()
{ {
var role = new Athlete(); var role = new Athlete();
var user = new User("hello13", "dkjd.png", "John", "Doe", "john@exemple.com", "password", "M", 180.5f, 75.3f, new System.DateTime(1990, 5, 15), new Athlete()); var user = new User("hello13", "dkjd.png", "John", "Doe", "john@exemple.com", "password", 'M', 180.5f, 75.3f, new System.DateTime(1990, 5, 15), new Athlete());
bool result = role.AddUser(user); bool result = role.AddUser(user);

@ -13,7 +13,7 @@ namespace Model.Tests
string firstName = "John"; string firstName = "John";
string email = "john.doe@example.com"; string email = "john.doe@example.com";
string password = "password"; string password = "password";
string sex = "Male"; char sex = 'M';
float length = 180.5f; float length = 180.5f;
float weight = 75.3f; float weight = 75.3f;
DateTime dateOfBirth = new DateTime(1990, 5, 15); DateTime dateOfBirth = new DateTime(1990, 5, 15);

@ -0,0 +1,129 @@
/*/*!
* \file BookDataServiceAPI.cs
* \author HeartTeam
* \brief Fichier contenant la classe BookDataServiceAPI.
#1#
using System.Diagnostics;
using Dto;
using Model.Repository;
using Shared;
using APIMappers;
using Dto.Tiny;
using Model;
namespace WebAPIConsoleTests;
/*!
* \brief Implémentation de l'interface IActivityRepository pour récupérer des activités via un service HTTP.
#1#
public class ActivityServiceAPI : IActivityRepository
{
private HttpRequest<ActivityDto> myRequest = new HttpRequest<ActivityDto>();
/*!
* \brief Constructeur de la classe ActivityServiceAPI.
* Initialise l'adresse de base du client HTTP.
#1#
public ActivityServiceAPI()
{
myRequest.HttpClient.BaseAddress = new Uri("http://localhost:5030/api/v1/Activity/");
}
/*!
* \brief Récupère toutes les Activités de manière asynchrone.
* \return Une tâche représentant l'opération asynchrone qui retourne une liste d'Activity.
#1#
public async Task<IEnumerable<Model.Activity>?> GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending = false)
{
var activityDtos = await myRequest.GetAllAsync();
return activityDtos?.ToModels();
}
/*!
* \brief Récupère les activités par index et compte de manière asynchrone.
* \param index L'index de départ pour la pagination.
* \param count Le nombre d'éléments à récupérer.
* \return Une tâche représentant l'opération asynchrone qui retourne une liste d'Activity.
#1#
public async Task<List<Model.Activity>> GetBooksAsync(ActivityOrderCriteria criteria, bool descending, int index, int count)
{
var activityDtos = await myRequest.GetAsync(criteria, descending, index, count);
return (List<Model.Activity>)activityDtos.ToModels();
}
/*!
* \brief Récupère une activité par son identifiant de manière asynchrone.
* \param id L'identifiant du livre à récupérer.
* \return Une tâche représentant l'opération asynchrone qui retourne une liste d'Activity.
#1#
async Task<IEnumerable<ActivityTinyDto>?> IActivityRepository.GetActivities(int index, int count, ActivityOrderCriteria criteria, bool descending)
{
throw new NotImplementedException();
}
public async Task<Model.Activity?> GetActivityByIdAsync(int id)
{
var activityDtos = await myRequest.GetByIdAsync(id);
return activityDtos.ToModel();
}
/*!
* \brief Ajoute une activité de manière asynchrone.
* \param activity L'Activity à ajouter.
* \return Une tâche représentant l'opération asynchrone qui retourne l'activité ajouté (Activity).
#1#
public async Task<Model.Activity?> AddActivity(Model.Activity activity)
{
return (await myRequest.PostAsync(activity.ToDto())).ToModel();
}
public async Task<ResponseActivityDto?> AddActivity(NewActivityDto activity)
{
throw new NotImplementedException();
}
public async Task<ResponseActivityDto?> UpdateActivity(int id, ActivityTinyDto activity)
{
throw new NotImplementedException();
}
/*!
* \brief Met à jour une activité de manière asynchrone.
* \param id L'identifiant de l'activité à mettre à jour.
* \param activity Les nouvelles données de l'activité à mettre à jour.
* \return Une tâche représentant l'opération asynchrone qui retourne l'activité mis à jour (Activity).
#1#
public async Task<Model.Activity?> UpdateActivity(int id, Model.Activity activity)
{
var activityDto = activity.ToDto();
var updatedActivityDto = await myRequest.PutAsync(id, activityDto);
return updatedActivityDto?.ToModel();
}
/*!
* \brief Supprime une activité de manière asynchrone.
* \param id L'identifiant de l'activité à supprimer.
* \return Une tâche représentant l'opération asynchrone.
#1#
public async Task<bool> DeleteActivity(int id)
{
await myRequest.DeleteAsync(id);
return true;
}
public Task<int> GetNbItems()
{
return myRequest.GetNbItems();
}
public async Task<IEnumerable<Model.Activity>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria orderCriteria, bool descending = false)
{
return (await myRequest.GetActivitiesByUser(userId, index, count, orderCriteria, descending)).ToModels();
}
public Task<int> GetNbActivitiesByUser(int userId)
{
return myRequest.GetNbActivitiesByUser(userId);
}
}*/

@ -0,0 +1,101 @@
/*!
* \file HttpRequest.cs
* \brief Fichier contenant la classe HttpRequest.
*/
using System.Diagnostics;
using System.Net.Http.Json;
using Dto;
using Shared;
namespace WebAPIConsoleTests;
/*!
* \brief Classe représentant un client HTTP pour les requêtes vers un service de gestion de elément.
*/
public class HttpRequest<T> where T : class
{
private HttpClient _httpClient { get; } = new HttpClient();
public HttpClient HttpClient => _httpClient;
/*!
* \brief Récupère tous les activitée de manière asynchrone.
* \return Une tâche représentant l'opération asynchrone qui retourne une liste de T.
*/
public async Task<List<T>> GetAllAsync()
{
return await _httpClient.GetFromJsonAsync<List<T>>("");
}
/*!
* \brief Récupère les élements par index et compte de manière asynchrone.
* \param index L'index de départ pour la pagination.
* \param count Le nombre d'éléments à récupérer.
* \return Une tâche représentant l'opération asynchrone qui retourne une liste de T.
*/
// [TODO] enum
public async Task<List<T>> GetAsync(Enum criteria, bool descending, int index, int count)
{
return await _httpClient.GetFromJsonAsync<List<T>>($"?OrderingPropertyName={criteria}&Descending={descending}&Index={index}&Count={count}");
}
/*!
* \brief Récupère un elément par son identifiant de manière asynchrone.
* \param id L'identifiant du elément à récupérer.
* \return Une tâche représentant l'opération asynchrone qui retourne une liste de T.
*/
public async Task<T?> GetByIdAsync(int id)
{
return await _httpClient.GetFromJsonAsync<T>($"{id}");
}
public Task<int> GetNbItems()
{
return _httpClient.GetFromJsonAsync<int>("count");
}
public Task<IEnumerable<T>?> GetActivitiesByUser(int userId, int index, int count, ActivityOrderCriteria orderCriteria, bool descending = false)
{
return _httpClient.GetFromJsonAsync<IEnumerable<T>?>($"?userId={userId}&index={index}&count={count}&orderCriteria={orderCriteria}&descending={descending}");
}
public Task<int> GetNbActivitiesByUser(int userId)
{
return _httpClient.GetFromJsonAsync<int>($"count?userId={userId}");
}
/*!
* \brief Ajoute une activity de manière asynchrone.
* \param book Le elément à ajouter.
* \return Une tâche représentant l'opération asynchrone qui retourne le activity ajouté (T).
*/
public async Task<T> PostAsync(T activity)
{
var response = await _httpClient.PostAsJsonAsync("", activity);
return await response.Content.ReadFromJsonAsync<T>();
}
/*!
* \brief Met à jour un elément de manière asynchrone.
* \param id L'identifiant du elément à mettre à jour.
* \param book Les nouvelles données du elément à mettre à jour.
* \return Une tâche représentant l'opération asynchrone qui retourne le elément mis à jour (T).
*/
public async Task<T> PutAsync(int id, T activity)
{
var response = await _httpClient.PutAsJsonAsync($"{id}", activity);
return await response.Content.ReadFromJsonAsync<T>();
}
/*!
* \brief Supprime un elément de manière asynchrone.
* \param id L'identifiant du elément à supprimer.
* \return Une tâche représentant l'opération asynchrone.
*/
public async Task DeleteAsync(int id)
{
await _httpClient.DeleteAsync($"{id}");
}
}

@ -0,0 +1,66 @@
using APIMappers;
using Dto;
using Model;
using Model.Repository;
using Shared;
using WebAPIConsoleTests;
Console.WriteLine("");
/*
IActivityRepository myConsoleTest = new ActivityServiceAPI();
// defini un delais d'attente du déploiement de l'API
await Task.Delay(5000);
// Affiche toutes les activités
Console.WriteLine("Affichage de toutes les Activités : ");
var res = await myConsoleTest.GetActivities(0, 10, ActivityOrderCriteria.ByAthleteId, false);
foreach(var myActivity in res)
{
Console.WriteLine(myActivity.Id + ", " + myActivity.Type + ", " + myActivity.StartTime + ", " + myActivity.EndTime + ", " + myActivity.DataSource + ", " + myActivity.Athlete);
}
// Affiche les activités par id
Console.WriteLine("Affichage du livre d'id 1 : ");
res = (IEnumerable<Model.Activity>)await myConsoleTest.GetActivityByIdAsync(1);
foreach (var myActivity in res)
{
Console.WriteLine(myActivity.Id + ", " + myActivity.Type + ", " + myActivity.StartTime + ", " + myActivity.EndTime + ", " + myActivity.DataSource + ", " + myActivity.Athlete);
}
// Ajouter une nouvelle activité
Console.WriteLine("Ajout d'un nouveau livre : ");
var newActivity = new ActivityDto { Type = "New Activity", StartTime = DateTime.Now, EndTime = DateTime.Now, DataSource = new DataSourceDto{}, Athlete = new UserDto{ Username = "Hello", FirstName = "feee", Email = "exemple.com", LastName = "dddd", Sexe = "M" } };
var addedActivity = await myConsoleTest.AddActivity(newActivity.ToModel());
Console.WriteLine($"Id: {addedActivity.Id}, Type: {addedActivity.Type}, StartTime: {addedActivity.StartTime}, EndTime: {addedActivity.EndTime}, DataSource: {addedActivity.DataSource}, Athlete: {addedActivity.Athlete}");
// Mettre à jour l'activity ajouté
Console.WriteLine("Mise à jour du livre ajouté : ");
var activity = await myConsoleTest.UpdateActivity(1, new ActivityDto { Id = 1, Type = "Updated Activity", StartTime = DateTime.Now, EndTime = DateTime.Now, DataSource = new DataSourceDto{}, Athlete = new UserDto{ Username = "Hello", FirstName = "feee", Email = "exemple.com", LastName = "dddd", Sexe = "M" } }.ToModel());
Console.WriteLine($"Id: {activity.Id}, Type: {activity.Type}, StartTime: {activity.StartTime}, EndTime: {activity.EndTime}, DataSource: {activity.DataSource}, Athlete: {activity.Athlete}");
// Supprimer l'activity ajouté
Console.WriteLine("Suppression du livre ajouté : ");
await myConsoleTest.DeleteActivity(newActivity.Id);
res = await myConsoleTest.GetActivities(0, 10, ActivityOrderCriteria.ByAthleteId, false);
foreach (var activity1 in res)
{
Console.WriteLine(activity1.Id + ", " + activity1.Type + ", " + activity1.StartTime + ", " + activity1.EndTime + ", " + activity1.DataSource + ", " + activity1.Athlete);
}
// Affiche le nombre d'activités
Console.WriteLine("Affichage du nombre d'activités : ");
var nb = await myConsoleTest.GetNbItems();
Console.WriteLine(nb);
// Affiche les activités par utilisateur
Console.WriteLine("Affichage des activités par utilisateur : ");
res = await myConsoleTest.GetActivitiesByUser(1, 0, 10, ActivityOrderCriteria.ByAthleteId, false);
foreach (var activity3 in res)
{
Console.WriteLine(activity3.Id + ", " + activity3.Type + ", " + activity3.StartTime + ", " + activity3.EndTime + ", " + activity3.DataSource + ", " + activity3.Athlete);
}
// Affiche le nombre d'activités par utilisateur
Console.WriteLine("Affichage du nombre d'activités par utilisateur : ");
nb = await myConsoleTest.GetNbActivitiesByUser(1);
Console.WriteLine(nb);*/

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\Dto\Dto.csproj" />
<ProjectReference Include="..\..\Shared\Shared.csproj" />
<ProjectReference Include="..\..\Model\Model.csproj" />
<ProjectReference Include="..\..\APIMappers\APIMappers.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Loading…
Cancel
Save