before rendu

This commit is contained in:
Mael G. 2024-03-27 15:20:10 +01:00
parent 89c17187ba
commit c84d872927
42 changed files with 261 additions and 83 deletions

9
Makefile Normal file
View file

@ -0,0 +1,9 @@
.PHONY: test
test:
pytest -v ./athlete/tests
pytest -v ./medaille/tests
pytest -v ./discipline/tests
clear_before_rendreTP:
rm -rf ./data ./__pycache__ ./athlete/__pycache__ ./medaille/__pycache__ ./discipline/__pycache__

View file

@ -34,7 +34,9 @@ python app.py
## How to test ## How to test
```bash ```bash
pytest -vvv . pytest -v athlete/tests
pytest -v discipline/tests
pytest -v medaille/tests
``` ```
## How to build the docker images ## How to build the docker images

View file

@ -41,7 +41,7 @@ class ListeAthlete(RootModel):
for athlete in data: for athlete in data:
self.root.append(Athlete(**athlete)) self.root.append(Athlete(**athlete))
except FileNotFoundError: except FileNotFoundError:
print("Fichier introuvable") print(f"Le fichier {path} n'existe pas")
def loadFromJsonData(self, data: str): def loadFromJsonData(self, data: str):
""" """
Charge les données depuis une chaine json Charge les données depuis une chaine json

View file

@ -2,6 +2,7 @@ import pytest
import shutil import shutil
from pathlib import Path from pathlib import Path
from app import create_app from app import create_app
from flask import request
@pytest.fixture() @pytest.fixture()
def app(): def app():

View file

@ -1,13 +1,14 @@
from flask import Flask, jsonify, request from flask import Flask, jsonify, request
from pathlib import Path from pathlib import Path
import json import json
from discipline import Discipline, ListeDiscipline from models import Discipline, ListeDiscipline
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from flask_swagger_ui import get_swaggerui_blueprint from flask_swagger_ui import get_swaggerui_blueprint
import os import os
app = Flask(__name__) app = Flask(__name__)
app.config['DISCIPLINE_FILE'] = os.getenv('DISCIPLINE_FILE', Path(__file__).parent.parent / 'data' / 'disciplines.json') app.config['DISCIPLINE_FILE'] = os.getenv('DISCIPLINE_FILE', Path(__file__).parent.parent / 'data' / 'disciplines.json')
print(app.config['DISCIPLINE_FILE'])
@app.route('/ping', methods=["GET"]) @app.route('/ping', methods=["GET"])
def ping(): def ping():
@ -50,9 +51,9 @@ def deleteDiscipline(id: int):
""" """
listeDisciplines = ListeDiscipline() listeDisciplines = ListeDiscipline()
listeDisciplines.loadFromJson(app.config['DISCIPLINE_FILE']) listeDisciplines.loadFromJson(app.config['DISCIPLINE_FILE'])
for athlete in listeDisciplines.root: for discipline in listeDisciplines.root:
if athlete.id == id: if discipline.id == id:
listeDisciplines.root.remove(athlete) listeDisciplines.root.remove(discipline)
with open(app.config['DISCIPLINE_FILE'], 'w') as f: with open(app.config['DISCIPLINE_FILE'], 'w') as f:
json.dump(listeDisciplines.model_dump(), f, indent=4) json.dump(listeDisciplines.model_dump(), f, indent=4)
return jsonify({"message": "Discipline supprimé"}), 200 return jsonify({"message": "Discipline supprimé"}), 200
@ -102,7 +103,7 @@ def addDiscipline():
listeDisciplines = ListeDiscipline() listeDisciplines = ListeDiscipline()
listeDisciplines.loadFromJson(app.config['DISCIPLINE_FILE']) listeDisciplines.loadFromJson(app.config['DISCIPLINE_FILE'])
discipline = Discipline(**json.loads(request.data)) discipline = Discipline(**json.loads(request.data))
discipline.id = max([athlete.id for athlete in listeDisciplines.root]) + 1 discipline.id = max([discipline.id for discipline in listeDisciplines.root]) + 1
listeDisciplines.root.append(discipline) listeDisciplines.root.append(discipline)
with open(app.config['DISCIPLINE_FILE'], 'w') as f: with open(app.config['DISCIPLINE_FILE'], 'w') as f:
json.dump(listeDisciplines.model_dump(), f, indent=4) json.dump(listeDisciplines.model_dump(), f, indent=4)

View file

@ -35,10 +35,10 @@ class ListeDiscipline(RootModel):
try: try:
with open(path) as f: with open(path) as f:
data = json.load(f) data = json.load(f)
for athlete in data: for discipline in data:
self.root.append(Discipline(**athlete)) self.root.append(Discipline(**discipline))
except FileNotFoundError: except FileNotFoundError:
print("Fichier introuvable") print(f"Le fichier {path} n'existe pas")
def loadFromJsonData(self, data: str): def loadFromJsonData(self, data: str):
""" """
Charge les données depuis une chaine json Charge les données depuis une chaine json
@ -46,5 +46,5 @@ class ListeDiscipline(RootModel):
:return: None :return: None
""" """
data = json.loads(data) data = json.loads(data)
for athlete in data: for discipline in data:
self.root.append(Discipline(**athlete)) self.root.append(Discipline(**discipline))

View file

@ -1,4 +1,4 @@
from discipline import Discipline from models import Discipline
def test_getDiscipline(client): def test_getDiscipline(client):
response = client.get("/1") response = client.get("/1")

View file

@ -1,9 +1,9 @@
from discipline import ListeDiscipline from models import ListeDiscipline
def test_listeAthlete(client): def test_listeDiscipline(client):
response = client.get("/") response = client.get("/")
listeDiscipline = ListeDiscipline() listeDiscipline = ListeDiscipline()
listeDiscipline.loadFromJsonData(response.data) listeDiscipline.loadFromJsonData(response.data)
assert listeDiscipline.root is not None assert listeDiscipline.root is not None
assert len(listeDiscipline.root) > 0 assert len(listeDiscipline.root) > 0
assert listeDiscipline.root[0].id == 0 assert listeDiscipline.root[0].id == 1
assert listeDiscipline.root[0].nom is not None assert listeDiscipline.root[0].intitule is not None

View file

@ -1,4 +1,4 @@
from discipline import Discipline from models import Discipline
def test_postDiscipline(client): def test_postDiscipline(client):
discipline = Discipline( discipline = Discipline(

View file

@ -1,7 +1,7 @@
from flask import Flask, jsonify, request from flask import Flask, jsonify, request
from pathlib import Path from pathlib import Path
import json import json
from athlete import ListeAthlete, Athlete from models import ListeMedaille, Medaille
from flask_swagger_ui import get_swaggerui_blueprint from flask_swagger_ui import get_swaggerui_blueprint
import os import os
@ -11,98 +11,102 @@ app.config['MEDAILLE_FILE'] = os.getenv('MEDAILLE_FILE', Path(__file__).parent.p
def ping(): def ping():
return jsonify({"message": "pong"}), 200 return jsonify({"message": "pong"}), 200
@app.route('/', methods=["GET"]) @app.route('/', methods=["GET"])
def listeAthlete(): def listeMedaille():
""" """
Renvoie la liste des athlètes Renvoie la liste des médilles
""" """
# Offset / Limit # Offset / Limit
offset = request.args.get('offset', 0) offset = request.args.get('offset', 0)
limit = request.args.get('limit', 10) limit = request.args.get('limit', 10)
listeAthletes = ListeAthlete() listeMedailles = ListeMedaille()
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE']) listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
if limit != 0: if limit != 0:
listeAthletes.root = listeAthletes.root[int(offset):int(offset)+int(limit)] listeMedailles.root = listeMedailles.root[int(offset):int(offset)+int(limit)]
else: else:
listeAthletes.root = listeAthletes.root[int(offset):] listeMedailles.root = listeMedailles.root[int(offset):]
return jsonify(listeAthletes.model_dump()), 200 return jsonify(listeMedailles.model_dump()), 200
@app.route('/<int:id>', methods=["GET"]) @app.route('/<int:id>', methods=["GET"])
def getAthlete(id: int): def getMedaille(id: int):
""" """
Renvoie un athlète par son id Renvoie un médille par son id
""" """
listeAthletes = ListeAthlete() listeMedailles = ListeMedaille()
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE']) listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
for athlete in listeAthletes.root: for medaille in listeMedailles.root:
if athlete.id == id: if medaille.id == id:
return jsonify(athlete.model_dump()), 200 return jsonify(medaille.model_dump()), 200
return jsonify({"message": "Athlete introuvable"}), 404 return jsonify({"message": "Medaille introuvable"}), 404
@app.route('/<int:id>', methods=["DELETE"]) @app.route('/<int:id>', methods=["DELETE"])
def deleteAthlete(id: int): def deleteMedaille(id: int):
""" """
Supprime un athlète par son id Supprime un médille par son id
""" """
listeAthletes = ListeAthlete() listeMedailles = ListeMedaille()
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE']) listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
for athlete in listeAthletes.root: for medaille in listeMedailles.root:
if athlete.id == id: if medaille.id == id:
listeAthletes.root.remove(athlete) listeMedailles.root.remove(medaille)
with open(app.config['MEDAILLE_FILE'], 'w') as f: with open(app.config['MEDAILLE_FILE'], 'w') as f:
json.dump(listeAthletes.model_dump(), f, indent=4) json.dump(listeMedailles.model_dump(), f, indent=4)
return jsonify({"message": "Athlete supprimé"}), 200 return jsonify({"message": "Medaille supprimé"}), 200
return jsonify({"message": "Athlete introuvable"}), 404 return jsonify({"message": "Medaille introuvable"}), 404
@app.route('/<int:id>', methods=["PUT"]) @app.route('/<int:id>', methods=["PUT"])
def updateAthlete(id: int): def updateMedaille(id: int):
""" """
Met à jour un athlète par son id Met à jour un médille par son id
""" """
listeAthletes = ListeAthlete() listeMedailles = ListeMedaille()
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE']) listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
for athlete in listeAthletes.root: for medaille in listeMedailles.root:
if athlete.id == id: if medaille.id == id:
data = json.loads(request.data) data = json.loads(request.data)
for key, value in data.items(): for key, value in data.items():
setattr(athlete, key, value) setattr(medaille, key, value)
with open(app.config['MEDAILLE_FILE'], 'w') as f: with open(app.config['MEDAILLE_FILE'], 'w') as f:
json.dump(listeAthletes.model_dump(), f, indent=4) json.dump(listeMedailles.model_dump(), f, indent=4)
return jsonify({"message": "Athlete mis à jour"}), 200 return jsonify({"message": "Medaille mis à jour"}), 200
return jsonify({"message": "Athlete introuvable"}), 404 return jsonify({"message": "Medaille introuvable"}), 404
@app.route('/<int:id>', methods=["PATCH"]) @app.route('/<int:id>', methods=["PATCH"])
def patchAthlete(id: int): def patchMedaille(id: int):
""" """
Met à jour un athlète par son id Met à jour un médille par son id
""" """
listeAthletes = ListeAthlete() listeMedailles = ListeMedaille()
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE']) listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
for athlete in listeAthletes.root: for medaille in listeMedailles.root:
if athlete.id == id: if medaille.id == id:
data = json.loads(request.data) data = json.loads(request.data)
data["id"] = athlete.id # On ne peut pas changer l'id data["id"] = medaille.id # On ne peut pas changer l'id
for key, value in data.items(): for key, value in data.items():
if hasattr(athlete, key): if hasattr(medaille, key):
setattr(athlete, key, value) setattr(medaille, key, value)
if not medaille.validate():
return jsonify({"message": "Données invalides"}), 400
with open(app.config['MEDAILLE_FILE'], 'w') as f: with open(app.config['MEDAILLE_FILE'], 'w') as f:
json.dump(listeAthletes.model_dump(), f, indent=4) json.dump(listeMedailles.model_dump(), f, indent=4)
return jsonify({"message": "Athlete mis à jour"}), 200 return jsonify({"message": "Medaille mis à jour"}), 200
return jsonify({"message": "Athlete introuvable"}), 404 return jsonify({"message": "Medaille introuvable"}), 404
@app.route('/', methods=["POST"]) @app.route('/', methods=["POST"])
def addAthlete(): def addMedaille():
""" """
Ajoute un athlète Ajoute un médille
""" """
listeAthletes = ListeAthlete() listeMedailles = ListeMedaille()
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE']) listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
athlete = Athlete(**json.loads(request.data)) medaille = Medaille(**json.loads(request.data))
athlete.id = max([athlete.id for athlete in listeAthletes.root]) + 1 if not medaille.validate():
listeAthletes.root.append(athlete) return jsonify({"message": "Données invalides"}), 400
medaille.id = max([medaille.id for medaille in listeMedailles.root]) + 1
listeMedailles.root.append(medaille)
with open(app.config['MEDAILLE_FILE'], 'w') as f: with open(app.config['MEDAILLE_FILE'], 'w') as f:
json.dump(listeAthletes.model_dump(), f, indent=4) json.dump(listeMedailles.model_dump(), f, indent=4)
return jsonify(athlete.model_dump()), 200 return jsonify(medaille.model_dump()), 200
swaggerui_blueprint = get_swaggerui_blueprint( swaggerui_blueprint = get_swaggerui_blueprint(
"/swagger", "/swagger",

60
medaille/models.py Normal file
View file

@ -0,0 +1,60 @@
from pydantic import BaseModel, RootModel
from typing import Optional, List
import json
class Medaille(BaseModel):
"""
Modèle Medaille
"""
id: Optional[int] = 0
type: str # Or, Argent, Bronze
sport: str
categorie: str # ex : 80kg pour le judo
pays: str # Nom FR du pays
def loadFromJsonData(self, data: str):
"""
Charge les données depuis une chaine json
:param data: Données json
:return: None
"""
data = json.loads(data)
for key, value in data.items():
setattr(self, key, value)
def validate(self) -> bool:
"""
Valide les données
:return: True si les données sont valides, False sinon
"""
if self.type not in ["Or", "Argent", "Bronze"]:
return False
return True
class ListeMedaille(RootModel):
root: List[Medaille] = []
def loadFromJson(self, path: str):
"""
Charge les données depuis un fichier json
:param path: Chemin du fichier json
:return: None
"""
try:
with open(path) as f:
data = json.load(f)
for discipline in data:
self.root.append(Medaille(**discipline))
except FileNotFoundError:
print(f"Le fichier {path} n'existe pas")
def loadFromJsonData(self, data: str):
"""
Charge les données depuis une chaine json
:param data: Données json
:return: None
"""
data = json.loads(data)
for discipline in data:
self.root.append(Medaille(**discipline))

View file

@ -118,15 +118,12 @@ components:
sport: sport:
type: integer type: integer
example: 1234567 example: 1234567
disclipine: categorie:
type: integer type: str
example: 1234567 example: +80kg
pays: pays:
type: string type: string
example: France example: France
logo:
type: string
example: https://olympics.com/images/static/sports/pictograms/v2/kte.svg
requestBodies: requestBodies:
User: User:
description: Objet médaille à ajouter description: Objet médaille à ajouter

View file

@ -0,0 +1,6 @@
def test_delMedaille(client):
response = client.delete("/1")
assert response.status_code == 200
response = client.get("/1")
assert response.status_code == 404

View file

@ -0,0 +1,13 @@
from models import Medaille
def test_getMedaille(client):
response = client.get("/1")
medaille = Medaille(
id=1,
type="Or",
sport="Judo",
categorie="80kg",
pays="France"
)
assert medaille.model_dump() == response.json
assert response.status_code == 200

View file

@ -0,0 +1,8 @@
from models import ListeMedaille
def test_listeMedaille(client):
response = client.get("/")
listeMedailles = ListeMedaille()
listeMedailles.loadFromJsonData(response.data)
assert listeMedailles.root is not None
assert len(listeMedailles.root) > 0
assert listeMedailles.root[0].id == 1

View file

@ -0,0 +1,3 @@
def test_ping_medaille(client):
response = client.get("/ping")
assert b"{\"message\":\"pong\"}\n" in response.data

View file

@ -0,0 +1,30 @@
from models import Medaille
def test_postMedaille(client):
medaille = Medaille(
id=1,
type="Argent",
sport="Escrime",
categorie="Fleuret",
pays="Italie"
)
response = client.post("/", json=medaille.model_dump())
assert response.status_code == 200
medaille.id = response.json["id"]
response = client.get(f"/{medaille.id}")
assert response.json == medaille.model_dump()
def test_postMedaille_erreur(client):
medaille = Medaille(
id=1,
type="NexistePas",
sport="Escrime",
categorie="Fleuret",
pays="Italie"
)
response = client.post("/", json=medaille.model_dump())
assert response.status_code == 400
assert response.json == {'message': 'Données invalides'}

View file

@ -18,13 +18,13 @@
"intitule": "100m", "intitule": "100m",
"type": "Course", "type": "Course",
"description": "Le 100 mètres est une épreuve de sprint en athlétisme. C'est l'épreuve reine des sprinteurs.", "description": "Le 100 mètres est une épreuve de sprint en athlétisme. C'est l'épreuve reine des sprinteurs.",
"logo": "https://upload.wikimedia.org/wikipedia/commons/1/1b/Athletics_pictogram.svg", "logo": "https://upload.wikimedia.org/wikipedia/commons/1/1b/Athletics_pictogram.svg"
}, },
{ {
"id": 4, "id": 4,
"intitule": "Natation", "intitule": "Natation",
"type": "Nage", "type": "Nage",
"description": "La natation est un sport consistant à parcourir une certaine distance dans l'eau, en utilisant les bras et les jambes.", "description": "La natation est un sport consistant à parcourir une certaine distance dans l'eau, en utilisant les bras et les jambes.",
"logo": "https://upload.wikimedia.org/wikipedia/commons/0/0d/Swimming_pictogram.svg", "logo": "https://upload.wikimedia.org/wikipedia/commons/0/0d/Swimming_pictogram.svg"
} }
] ]

44
sample/medailles.json Normal file
View file

@ -0,0 +1,44 @@
[
{
"id": 1,
"type": "Or",
"sport": "Judo",
"categorie": "80kg",
"pays": "France"
},
{
"id": 2,
"type": "Argent",
"sport": "Judo",
"categorie": "80kg",
"pays": "France"
},
{
"id": 3,
"type": "Bronze",
"sport": "Judo",
"categorie": "80kg",
"pays": "France"
},
{
"id": 4,
"type": "Or",
"sport": "Judo",
"categorie": "80kg",
"pays": "France"
},
{
"id": 5,
"type": "Argent",
"sport": "Judo",
"categorie": "80kg",
"pays": "France"
},
{
"id": 6,
"type": "Bronze",
"sport": "Judo",
"categorie": "80kg",
"pays": "France"
}
]