before rendu
This commit is contained in:
parent
89c17187ba
commit
c84d872927
42 changed files with 261 additions and 83 deletions
9
Makefile
Normal file
9
Makefile
Normal 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__
|
|
@ -34,7 +34,9 @@ python app.py
|
|||
|
||||
## How to test
|
||||
```bash
|
||||
pytest -vvv .
|
||||
pytest -v athlete/tests
|
||||
pytest -v discipline/tests
|
||||
pytest -v medaille/tests
|
||||
```
|
||||
|
||||
## How to build the docker images
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -41,7 +41,7 @@ class ListeAthlete(RootModel):
|
|||
for athlete in data:
|
||||
self.root.append(Athlete(**athlete))
|
||||
except FileNotFoundError:
|
||||
print("Fichier introuvable")
|
||||
print(f"Le fichier {path} n'existe pas")
|
||||
def loadFromJsonData(self, data: str):
|
||||
"""
|
||||
Charge les données depuis une chaine json
|
||||
|
|
|
@ -2,6 +2,7 @@ import pytest
|
|||
import shutil
|
||||
from pathlib import Path
|
||||
from app import create_app
|
||||
from flask import request
|
||||
|
||||
@pytest.fixture()
|
||||
def app():
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,13 +1,14 @@
|
|||
from flask import Flask, jsonify, request
|
||||
from pathlib import Path
|
||||
import json
|
||||
from discipline import Discipline, ListeDiscipline
|
||||
from models import Discipline, ListeDiscipline
|
||||
# noinspection PyUnresolvedReferences
|
||||
from flask_swagger_ui import get_swaggerui_blueprint
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
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"])
|
||||
def ping():
|
||||
|
@ -50,9 +51,9 @@ def deleteDiscipline(id: int):
|
|||
"""
|
||||
listeDisciplines = ListeDiscipline()
|
||||
listeDisciplines.loadFromJson(app.config['DISCIPLINE_FILE'])
|
||||
for athlete in listeDisciplines.root:
|
||||
if athlete.id == id:
|
||||
listeDisciplines.root.remove(athlete)
|
||||
for discipline in listeDisciplines.root:
|
||||
if discipline.id == id:
|
||||
listeDisciplines.root.remove(discipline)
|
||||
with open(app.config['DISCIPLINE_FILE'], 'w') as f:
|
||||
json.dump(listeDisciplines.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Discipline supprimé"}), 200
|
||||
|
@ -102,7 +103,7 @@ def addDiscipline():
|
|||
listeDisciplines = ListeDiscipline()
|
||||
listeDisciplines.loadFromJson(app.config['DISCIPLINE_FILE'])
|
||||
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)
|
||||
with open(app.config['DISCIPLINE_FILE'], 'w') as f:
|
||||
json.dump(listeDisciplines.model_dump(), f, indent=4)
|
||||
|
|
|
@ -35,10 +35,10 @@ class ListeDiscipline(RootModel):
|
|||
try:
|
||||
with open(path) as f:
|
||||
data = json.load(f)
|
||||
for athlete in data:
|
||||
self.root.append(Discipline(**athlete))
|
||||
for discipline in data:
|
||||
self.root.append(Discipline(**discipline))
|
||||
except FileNotFoundError:
|
||||
print("Fichier introuvable")
|
||||
print(f"Le fichier {path} n'existe pas")
|
||||
def loadFromJsonData(self, data: str):
|
||||
"""
|
||||
Charge les données depuis une chaine json
|
||||
|
@ -46,5 +46,5 @@ class ListeDiscipline(RootModel):
|
|||
:return: None
|
||||
"""
|
||||
data = json.loads(data)
|
||||
for athlete in data:
|
||||
self.root.append(Discipline(**athlete))
|
||||
for discipline in data:
|
||||
self.root.append(Discipline(**discipline))
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
from discipline import Discipline
|
||||
from models import Discipline
|
||||
|
||||
def test_getDiscipline(client):
|
||||
response = client.get("/1")
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from discipline import ListeDiscipline
|
||||
def test_listeAthlete(client):
|
||||
from models import ListeDiscipline
|
||||
def test_listeDiscipline(client):
|
||||
response = client.get("/")
|
||||
listeDiscipline = ListeDiscipline()
|
||||
listeDiscipline.loadFromJsonData(response.data)
|
||||
assert listeDiscipline.root is not None
|
||||
assert len(listeDiscipline.root) > 0
|
||||
assert listeDiscipline.root[0].id == 0
|
||||
assert listeDiscipline.root[0].nom is not None
|
||||
assert listeDiscipline.root[0].id == 1
|
||||
assert listeDiscipline.root[0].intitule is not None
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from discipline import Discipline
|
||||
from models import Discipline
|
||||
|
||||
def test_postDiscipline(client):
|
||||
discipline = Discipline(
|
||||
|
|
Binary file not shown.
118
medaille/app.py
118
medaille/app.py
|
@ -1,7 +1,7 @@
|
|||
from flask import Flask, jsonify, request
|
||||
from pathlib import Path
|
||||
import json
|
||||
from athlete import ListeAthlete, Athlete
|
||||
from models import ListeMedaille, Medaille
|
||||
from flask_swagger_ui import get_swaggerui_blueprint
|
||||
import os
|
||||
|
||||
|
@ -11,98 +11,102 @@ app.config['MEDAILLE_FILE'] = os.getenv('MEDAILLE_FILE', Path(__file__).parent.p
|
|||
def ping():
|
||||
return jsonify({"message": "pong"}), 200
|
||||
@app.route('/', methods=["GET"])
|
||||
def listeAthlete():
|
||||
def listeMedaille():
|
||||
"""
|
||||
Renvoie la liste des athlètes
|
||||
Renvoie la liste des médilles
|
||||
"""
|
||||
# Offset / Limit
|
||||
offset = request.args.get('offset', 0)
|
||||
limit = request.args.get('limit', 10)
|
||||
listeAthletes = ListeAthlete()
|
||||
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
listeMedailles = ListeMedaille()
|
||||
listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
|
||||
if limit != 0:
|
||||
listeAthletes.root = listeAthletes.root[int(offset):int(offset)+int(limit)]
|
||||
listeMedailles.root = listeMedailles.root[int(offset):int(offset)+int(limit)]
|
||||
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"])
|
||||
def getAthlete(id: int):
|
||||
def getMedaille(id: int):
|
||||
"""
|
||||
Renvoie un athlète par son id
|
||||
Renvoie un médille par son id
|
||||
"""
|
||||
listeAthletes = ListeAthlete()
|
||||
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for athlete in listeAthletes.root:
|
||||
if athlete.id == id:
|
||||
return jsonify(athlete.model_dump()), 200
|
||||
return jsonify({"message": "Athlete introuvable"}), 404
|
||||
listeMedailles = ListeMedaille()
|
||||
listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for medaille in listeMedailles.root:
|
||||
if medaille.id == id:
|
||||
return jsonify(medaille.model_dump()), 200
|
||||
return jsonify({"message": "Medaille introuvable"}), 404
|
||||
@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()
|
||||
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for athlete in listeAthletes.root:
|
||||
if athlete.id == id:
|
||||
listeAthletes.root.remove(athlete)
|
||||
listeMedailles = ListeMedaille()
|
||||
listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for medaille in listeMedailles.root:
|
||||
if medaille.id == id:
|
||||
listeMedailles.root.remove(medaille)
|
||||
with open(app.config['MEDAILLE_FILE'], 'w') as f:
|
||||
json.dump(listeAthletes.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Athlete supprimé"}), 200
|
||||
return jsonify({"message": "Athlete introuvable"}), 404
|
||||
json.dump(listeMedailles.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Medaille supprimé"}), 200
|
||||
return jsonify({"message": "Medaille introuvable"}), 404
|
||||
|
||||
@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()
|
||||
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for athlete in listeAthletes.root:
|
||||
if athlete.id == id:
|
||||
listeMedailles = ListeMedaille()
|
||||
listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for medaille in listeMedailles.root:
|
||||
if medaille.id == id:
|
||||
data = json.loads(request.data)
|
||||
for key, value in data.items():
|
||||
setattr(athlete, key, value)
|
||||
setattr(medaille, key, value)
|
||||
with open(app.config['MEDAILLE_FILE'], 'w') as f:
|
||||
json.dump(listeAthletes.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Athlete mis à jour"}), 200
|
||||
return jsonify({"message": "Athlete introuvable"}), 404
|
||||
json.dump(listeMedailles.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Medaille mis à jour"}), 200
|
||||
return jsonify({"message": "Medaille introuvable"}), 404
|
||||
|
||||
@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()
|
||||
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for athlete in listeAthletes.root:
|
||||
if athlete.id == id:
|
||||
listeMedailles = ListeMedaille()
|
||||
listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
for medaille in listeMedailles.root:
|
||||
if medaille.id == id:
|
||||
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():
|
||||
if hasattr(athlete, key):
|
||||
setattr(athlete, key, value)
|
||||
if hasattr(medaille, key):
|
||||
setattr(medaille, key, value)
|
||||
if not medaille.validate():
|
||||
return jsonify({"message": "Données invalides"}), 400
|
||||
with open(app.config['MEDAILLE_FILE'], 'w') as f:
|
||||
json.dump(listeAthletes.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Athlete mis à jour"}), 200
|
||||
return jsonify({"message": "Athlete introuvable"}), 404
|
||||
json.dump(listeMedailles.model_dump(), f, indent=4)
|
||||
return jsonify({"message": "Medaille mis à jour"}), 200
|
||||
return jsonify({"message": "Medaille introuvable"}), 404
|
||||
|
||||
@app.route('/', methods=["POST"])
|
||||
def addAthlete():
|
||||
def addMedaille():
|
||||
"""
|
||||
Ajoute un athlète
|
||||
Ajoute un médille
|
||||
"""
|
||||
listeAthletes = ListeAthlete()
|
||||
listeAthletes.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
athlete = Athlete(**json.loads(request.data))
|
||||
athlete.id = max([athlete.id for athlete in listeAthletes.root]) + 1
|
||||
listeAthletes.root.append(athlete)
|
||||
listeMedailles = ListeMedaille()
|
||||
listeMedailles.loadFromJson(app.config['MEDAILLE_FILE'])
|
||||
medaille = Medaille(**json.loads(request.data))
|
||||
if not medaille.validate():
|
||||
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:
|
||||
json.dump(listeAthletes.model_dump(), f, indent=4)
|
||||
return jsonify(athlete.model_dump()), 200
|
||||
json.dump(listeMedailles.model_dump(), f, indent=4)
|
||||
return jsonify(medaille.model_dump()), 200
|
||||
|
||||
swaggerui_blueprint = get_swaggerui_blueprint(
|
||||
"/swagger",
|
||||
|
|
60
medaille/models.py
Normal file
60
medaille/models.py
Normal 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))
|
|
@ -118,15 +118,12 @@ components:
|
|||
sport:
|
||||
type: integer
|
||||
example: 1234567
|
||||
disclipine:
|
||||
type: integer
|
||||
example: 1234567
|
||||
categorie:
|
||||
type: str
|
||||
example: +80kg
|
||||
pays:
|
||||
type: string
|
||||
example: France
|
||||
logo:
|
||||
type: string
|
||||
example: https://olympics.com/images/static/sports/pictograms/v2/kte.svg
|
||||
requestBodies:
|
||||
User:
|
||||
description: Objet médaille à ajouter
|
||||
|
|
6
medaille/tests/test_delMedaille.py
Normal file
6
medaille/tests/test_delMedaille.py
Normal 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
|
13
medaille/tests/test_getMedaille.py
Normal file
13
medaille/tests/test_getMedaille.py
Normal 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
|
8
medaille/tests/test_listeMedaille.py
Normal file
8
medaille/tests/test_listeMedaille.py
Normal 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
|
3
medaille/tests/test_ping_medaille.py
Normal file
3
medaille/tests/test_ping_medaille.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
def test_ping_medaille(client):
|
||||
response = client.get("/ping")
|
||||
assert b"{\"message\":\"pong\"}\n" in response.data
|
30
medaille/tests/test_postMedaille.py
Normal file
30
medaille/tests/test_postMedaille.py
Normal 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'}
|
|
@ -18,13 +18,13 @@
|
|||
"intitule": "100m",
|
||||
"type": "Course",
|
||||
"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,
|
||||
"intitule": "Natation",
|
||||
"type": "Nage",
|
||||
"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
44
sample/medailles.json
Normal 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"
|
||||
}
|
||||
]
|
Loading…
Reference in a new issue