Demosthene-OR
commited on
Commit
•
5afe693
1
Parent(s):
40486e6
complement
Browse files- Dockerfile +1 -1
- app.py +0 -189
- credentials.csv +0 -95
- main.py +183 -0
- openapi.json +1 -0
- questions.csv +78 -0
- requirements.txt +21 -4
Dockerfile
CHANGED
@@ -8,4 +8,4 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
|
8 |
|
9 |
COPY . .
|
10 |
|
11 |
-
CMD ["uvicorn", "app.main:
|
|
|
8 |
|
9 |
COPY . .
|
10 |
|
11 |
+
CMD ["uvicorn", "app.main:api", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
DELETED
@@ -1,189 +0,0 @@
|
|
1 |
-
from flask import Flask, request, jsonify, make_response
|
2 |
-
from flasgger import Swagger, swag_from
|
3 |
-
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
|
4 |
-
import random
|
5 |
-
import csv
|
6 |
-
|
7 |
-
app = Flask(__name__)
|
8 |
-
swagger = Swagger(app)
|
9 |
-
|
10 |
-
# Définition du titre de la documentation Swagger
|
11 |
-
app.config['SWAGGER'] = {
|
12 |
-
'title': 'API Analyse de sentiment',
|
13 |
-
'description': 'Documentation de l\'API pour l\'analyse de sentiment',
|
14 |
-
'version': '1.0.0'
|
15 |
-
}
|
16 |
-
|
17 |
-
# Charger les informations d'authentification à partir du fichier credentials.csv
|
18 |
-
def load_credentials(file_path):
|
19 |
-
credentials = {}
|
20 |
-
with open(file_path, newline='') as csvfile:
|
21 |
-
reader = csv.DictReader(csvfile)
|
22 |
-
for row in reader:
|
23 |
-
credentials[row['username']] = {'password': row['password'], 'v1': int(row['v1']), 'v2': int(row['v2'])}
|
24 |
-
return credentials
|
25 |
-
|
26 |
-
credentials = load_credentials('credentials.csv')
|
27 |
-
|
28 |
-
# Route pour vérifier l'état de l'API
|
29 |
-
@app.route('/status')
|
30 |
-
def status():
|
31 |
-
"""
|
32 |
-
API Status
|
33 |
-
---
|
34 |
-
responses:
|
35 |
-
200:
|
36 |
-
description: API is working
|
37 |
-
examples:
|
38 |
-
message: 1
|
39 |
-
"""
|
40 |
-
return '1'
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
# Route pour saluer l'utilisateur
|
45 |
-
@app.route('/welcome')
|
46 |
-
def welcome():
|
47 |
-
"""
|
48 |
-
Message de bienvenue
|
49 |
-
---
|
50 |
-
parameters:
|
51 |
-
- name: username
|
52 |
-
in: query
|
53 |
-
type: string
|
54 |
-
required: true
|
55 |
-
description: Message de bienvenue à l'utilisateur
|
56 |
-
responses:
|
57 |
-
200:
|
58 |
-
description: Message
|
59 |
-
examples:
|
60 |
-
message: Bienvenue sur l'API d'Analyse de sentiment, Olivier!
|
61 |
-
"""
|
62 |
-
username = request.args.get('username')
|
63 |
-
return f"Bienvenue sur l'API d'Analyse de sentiment {username}!"
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
# Route pour vérifier les credentials de l'utilisateur
|
68 |
-
@app.route('/credentials', methods=['POST'])
|
69 |
-
def check_credentials():
|
70 |
-
"""
|
71 |
-
credentials Route
|
72 |
-
---
|
73 |
-
consumes:
|
74 |
-
- application/json
|
75 |
-
parameters:
|
76 |
-
- in: header
|
77 |
-
name: Authorization
|
78 |
-
type: string
|
79 |
-
required: true
|
80 |
-
description: The user's credentials in the format "username=password"
|
81 |
-
responses:
|
82 |
-
200:
|
83 |
-
description: A list of user credentials
|
84 |
-
examples:
|
85 |
-
username: John
|
86 |
-
v1: 1
|
87 |
-
v2: 0
|
88 |
-
"""
|
89 |
-
auth = request.headers.get('Authorization')
|
90 |
-
if not auth:
|
91 |
-
return 'Unauthorized', 401
|
92 |
-
|
93 |
-
username, password = auth.split('=')
|
94 |
-
if username not in credentials or credentials[username]['password'] != password:
|
95 |
-
return 'Unauthorized', 401
|
96 |
-
|
97 |
-
response_data = {'username': username, 'v1': credentials[username]['v1'], 'v2': credentials[username]['v2']}
|
98 |
-
response = make_response(jsonify(response_data), 200)
|
99 |
-
response.headers['Content-Type'] = 'application/json'
|
100 |
-
return response
|
101 |
-
|
102 |
-
|
103 |
-
# Route pour l'analyse de sentiment avec le modèle v1
|
104 |
-
@app.route('/v1/sentiment', methods=['POST'])
|
105 |
-
def sentiment_v1():
|
106 |
-
"""
|
107 |
-
Sentiment Analysis Route (Model v1)
|
108 |
-
---
|
109 |
-
parameters:
|
110 |
-
- in: header
|
111 |
-
name: Authorization
|
112 |
-
type: string
|
113 |
-
required: true
|
114 |
-
description: The user's credentials in the format "username=password"
|
115 |
-
- in: formData # Modification ici pour spécifier le type de données du formulaire
|
116 |
-
name: sentence
|
117 |
-
type: string
|
118 |
-
required: true
|
119 |
-
description: The sentence to analyze
|
120 |
-
responses:
|
121 |
-
200:
|
122 |
-
description: The sentiment score
|
123 |
-
examples:
|
124 |
-
score: 0.5
|
125 |
-
"""
|
126 |
-
auth = request.headers.get('Authorization')
|
127 |
-
if not auth:
|
128 |
-
return 'Unauthorized', 401
|
129 |
-
|
130 |
-
username, password = auth.split('=')
|
131 |
-
if username not in credentials or credentials[username]['password'] != password:
|
132 |
-
return 'Unauthorized (wrong username and/or password)', 401
|
133 |
-
|
134 |
-
if credentials[username]['v1']==0:
|
135 |
-
return 'User unauthorized for Sentiment Analysis v1', 401
|
136 |
-
|
137 |
-
sentence = request.form.get('sentence')
|
138 |
-
if not sentence:
|
139 |
-
return 'Bad Request', 400
|
140 |
-
|
141 |
-
# Implémentation factice de l'analyse de sentiment pour le modèle v1 (nombre aléatoire entre -1 et 1)
|
142 |
-
sentiment_score = random.uniform(-1, 1)
|
143 |
-
return str(sentiment_score)
|
144 |
-
|
145 |
-
# Route pour l'analyse de sentiment avec le modèle v2
|
146 |
-
@app.route('/v2/sentiment', methods=['POST'])
|
147 |
-
def sentiment_v2():
|
148 |
-
"""
|
149 |
-
Sentiment Analysis Route (Model v2)
|
150 |
-
---
|
151 |
-
parameters:
|
152 |
-
- in: header
|
153 |
-
name: Authorization
|
154 |
-
type: string
|
155 |
-
required: true
|
156 |
-
description: The user's credentials in the format "username=password"
|
157 |
-
- in: formData # Modification ici pour spécifier le type de données du formulaire
|
158 |
-
name: sentence
|
159 |
-
type: string
|
160 |
-
required: true
|
161 |
-
description: The sentence to analyze
|
162 |
-
responses:
|
163 |
-
200:
|
164 |
-
description: The sentiment score
|
165 |
-
examples:
|
166 |
-
score: 0.5
|
167 |
-
"""
|
168 |
-
auth = request.headers.get('Authorization')
|
169 |
-
if not auth:
|
170 |
-
return 'Unauthorized', 401
|
171 |
-
|
172 |
-
username, password = auth.split('=')
|
173 |
-
if username not in credentials or credentials[username]['password'] != password:
|
174 |
-
return 'Unauthorized (wrong username and/or password)', 401
|
175 |
-
|
176 |
-
if credentials[username]['v2']==0:
|
177 |
-
return 'User unauthorized for Sentiment Analysis v2', 401
|
178 |
-
|
179 |
-
sentence = request.form.get('sentence')
|
180 |
-
if not sentence:
|
181 |
-
return 'Bad Request', 400
|
182 |
-
|
183 |
-
# Implémentation de l'analyse de sentiment avec le modèle v2 (VaderSentiment)
|
184 |
-
analyzer = SentimentIntensityAnalyzer()
|
185 |
-
sentiment_score = analyzer.polarity_scores(sentence)['compound']
|
186 |
-
return str(sentiment_score)
|
187 |
-
|
188 |
-
if __name__ == '__main__':
|
189 |
-
app.run(host="0.0.0.0",debug=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
credentials.csv
DELETED
@@ -1,95 +0,0 @@
|
|
1 |
-
username,password,v1,v2
|
2 |
-
Quinlan,5210,0,1
|
3 |
-
Davis,5783,0,0
|
4 |
-
Montana,3134,0,0
|
5 |
-
Quintessa,8790,0,1
|
6 |
-
Camden,4837,0,0
|
7 |
-
Megan,6837,1,1
|
8 |
-
Liberty,5564,1,1
|
9 |
-
Zelda,8610,0,1
|
10 |
-
Mara,9820,1,0
|
11 |
-
Steven,5998,1,1
|
12 |
-
Rhiannon,3545,1,0
|
13 |
-
Julian,7520,0,1
|
14 |
-
Anika,8944,0,1
|
15 |
-
Justine,9156,1,0
|
16 |
-
Lionel,4527,0,1
|
17 |
-
Yoshi,4237,1,1
|
18 |
-
Brian,7263,1,1
|
19 |
-
Leila,8308,0,1
|
20 |
-
Piper,2914,1,0
|
21 |
-
Yen,3932,1,0
|
22 |
-
Amber,9274,1,1
|
23 |
-
Rhonda,6818,1,0
|
24 |
-
Lamar,6478,0,0
|
25 |
-
Nolan,4611,1,1
|
26 |
-
Ulla,3610,0,1
|
27 |
-
Ginger,6723,0,1
|
28 |
-
Geraldine,5725,0,1
|
29 |
-
Dominic,7914,1,1
|
30 |
-
Paula,8403,1,0
|
31 |
-
Hashim,7658,1,1
|
32 |
-
Demetria,2132,1,1
|
33 |
-
Wang,7829,1,0
|
34 |
-
Tyler,4453,0,0
|
35 |
-
Kim,7134,0,1
|
36 |
-
Edward,1871,1,1
|
37 |
-
Russell,3039,0,0
|
38 |
-
Malachi,5226,1,0
|
39 |
-
Barrett,2891,1,0
|
40 |
-
Harriet,6877,0,0
|
41 |
-
Portia,8105,0,0
|
42 |
-
Raya,3006,1,1
|
43 |
-
Reed,1745,0,1
|
44 |
-
Penelope,4844,1,0
|
45 |
-
Whilemina,6551,1,1
|
46 |
-
Fay,9208,0,0
|
47 |
-
Shad,4903,0,0
|
48 |
-
Pandora,2598,1,1
|
49 |
-
Abbot,3545,0,0
|
50 |
-
Ciaran,2336,0,1
|
51 |
-
Nehru,8209,1,0
|
52 |
-
Logan,9622,0,1
|
53 |
-
Vernon,1422,0,1
|
54 |
-
Gavin,9632,0,1
|
55 |
-
Dana,5425,1,0
|
56 |
-
Ashton,1128,0,0
|
57 |
-
Mark,4608,1,1
|
58 |
-
Akeem,7908,0,0
|
59 |
-
Richard,2816,1,1
|
60 |
-
Colby,2427,1,1
|
61 |
-
Yasir,9460,1,0
|
62 |
-
Octavia,6792,1,1
|
63 |
-
Noel,3409,0,1
|
64 |
-
Michael,3341,0,0
|
65 |
-
Joel,3447,0,1
|
66 |
-
Stacy,9140,1,0
|
67 |
-
Mason,6588,1,1
|
68 |
-
Kelsey,3801,0,1
|
69 |
-
Daria,3036,1,1
|
70 |
-
Joy,1306,1,1
|
71 |
-
Xantha,8532,0,0
|
72 |
-
Lael,4088,1,1
|
73 |
-
Ursa,5331,1,0
|
74 |
-
Hayes,5297,0,1
|
75 |
-
Neve,4972,1,0
|
76 |
-
Bradley,6463,1,1
|
77 |
-
Sonya,7745,0,0
|
78 |
-
Adam,5910,0,1
|
79 |
-
Alfonso,8611,1,1
|
80 |
-
Jin,4199,0,1
|
81 |
-
Jared,2126,0,0
|
82 |
-
Constance,6624,1,1
|
83 |
-
Myra,2262,1,1
|
84 |
-
Emerald,5253,0,0
|
85 |
-
Remedios,2766,1,1
|
86 |
-
Shannon,2704,0,0
|
87 |
-
Steel,2034,1,0
|
88 |
-
Xena,7153,1,0
|
89 |
-
Ann,3068,0,1
|
90 |
-
Hadassah,3371,0,1
|
91 |
-
Xavier,8313,0,0
|
92 |
-
Hall,5494,1,0
|
93 |
-
Conan,3708,0,0
|
94 |
-
Buffy,5225,1,0
|
95 |
-
Chava,2094,0,1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
main.py
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, HTTPException, Header, Depends, Request
|
2 |
+
from fastapi.responses import JSONResponse
|
3 |
+
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
4 |
+
from fastapi.exceptions import RequestValidationError
|
5 |
+
from typing import Optional, List
|
6 |
+
from pydantic import BaseModel, ValidationError
|
7 |
+
import pandas as pd
|
8 |
+
import datetime
|
9 |
+
|
10 |
+
api = FastAPI()
|
11 |
+
|
12 |
+
# Charger les données à partir du fichier CSV
|
13 |
+
questions_data = pd.read_csv('questions.csv')
|
14 |
+
|
15 |
+
# Dictionnaire des identifiants des utilisateurs
|
16 |
+
users_credentials = {
|
17 |
+
"alice": "wonderland",
|
18 |
+
"bob": "builder",
|
19 |
+
"clementine": "mandarine",
|
20 |
+
"admin": "4dm1N" # Ajout de l'utilisateur admin
|
21 |
+
}
|
22 |
+
|
23 |
+
# Modèle Pydantic pour représenter une question
|
24 |
+
class Question(BaseModel):
|
25 |
+
question: str
|
26 |
+
subject: str
|
27 |
+
correct: Optional[str] = None # Champ optionnel
|
28 |
+
use: str
|
29 |
+
responseA: str
|
30 |
+
responseB: str
|
31 |
+
responseC: Optional[str] = None # Champ optionnel
|
32 |
+
responseD: Optional[str] = None # Champ optionnel
|
33 |
+
# remark: Optional[str] = None # Champ optionnel
|
34 |
+
|
35 |
+
# Modèle pour représenter une exception personnalisée
|
36 |
+
class MyException(Exception):
|
37 |
+
def __init__(self,
|
38 |
+
status_code: int,
|
39 |
+
name : str,
|
40 |
+
message : str):
|
41 |
+
self.status_code = status_code
|
42 |
+
self.name = name
|
43 |
+
self.message = message
|
44 |
+
self.date = str(datetime.datetime.now())
|
45 |
+
|
46 |
+
# Gestionnaire d'exception personnalisé
|
47 |
+
@api.exception_handler(MyException)
|
48 |
+
def MyExceptionHandler(
|
49 |
+
request: Request,
|
50 |
+
exception: MyException
|
51 |
+
):
|
52 |
+
return JSONResponse(
|
53 |
+
status_code=exception.status_code,
|
54 |
+
content={
|
55 |
+
'url': str(request.url),
|
56 |
+
'name': exception.name,
|
57 |
+
'message': exception.message,
|
58 |
+
'date': exception.date
|
59 |
+
}
|
60 |
+
)
|
61 |
+
|
62 |
+
# Gestionnaire d'exception pour les erreurs de validation de la requête
|
63 |
+
@api.exception_handler(RequestValidationError)
|
64 |
+
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
65 |
+
return JSONResponse(
|
66 |
+
status_code=422,
|
67 |
+
content={
|
68 |
+
'url': str(request.url),
|
69 |
+
'name': "Erreur de validation de la requête (parametre requis)",
|
70 |
+
'message': exc.errors(),
|
71 |
+
'date': str(datetime.datetime.now())
|
72 |
+
},
|
73 |
+
)
|
74 |
+
|
75 |
+
# Gestionnaire d'exception pour les erreurs Pydantic
|
76 |
+
@api.exception_handler(ValidationError)
|
77 |
+
async def validation_exception_handler(request: Request, exc: ValidationError):
|
78 |
+
return JSONResponse(
|
79 |
+
status_code=422,
|
80 |
+
content={
|
81 |
+
'url': str(request.url),
|
82 |
+
'name': "Erreur de validation Pydantic",
|
83 |
+
'message': exc.errors(),
|
84 |
+
'date': str(datetime.datetime.now())
|
85 |
+
},
|
86 |
+
)
|
87 |
+
|
88 |
+
|
89 |
+
# Fonction pour vérifier l'authentification de l'utilisateur
|
90 |
+
def authenticate(authorization: str = Header(None)):
|
91 |
+
if not authorization:
|
92 |
+
raise HTTPException(status_code=401, detail="Utilisateur non authorisé")
|
93 |
+
try:
|
94 |
+
scheme, credentials = authorization.split()
|
95 |
+
if scheme != 'Basic':
|
96 |
+
raise HTTPException(status_code=401, detail="Utilisateur non authorisé")
|
97 |
+
username, password = credentials.split(":")
|
98 |
+
if username not in users_credentials or users_credentials[username] != password:
|
99 |
+
raise HTTPException(status_code=401, detail="Utilisateur non authorisé")
|
100 |
+
except Exception as e:
|
101 |
+
raise HTTPException(status_code=401, detail="Utilisateur non authorisé")
|
102 |
+
return username, password
|
103 |
+
|
104 |
+
# Endpoint pour vérifier que l'API est fonctionnelle
|
105 |
+
@api.get('/', name="Vérification que l'API fonctionne")
|
106 |
+
def check_api():
|
107 |
+
return {'message': "L'API fonctionne"}
|
108 |
+
|
109 |
+
# Endpoint pour récupérer les questions en fonction du type de test (use) et des catégories (subject) spécifiés
|
110 |
+
@api.get('/questions', name="Récupération des questions pour un QCM")
|
111 |
+
def get_questions(use: str,
|
112 |
+
subject: str,
|
113 |
+
num_questions: int,
|
114 |
+
auth_info: tuple = Depends(authenticate)):
|
115 |
+
"""
|
116 |
+
Récupère les questions en fonction du type de test (use) et des catégories (subject) spécifiés
|
117 |
+
L'application peut produire des QCMs de 5, 10 ou 20 questions (seulement)
|
118 |
+
Les questions sont retournées dans un ordre aléatoire
|
119 |
+
Seuls les utilisateurs se trouvant dans users_credentials peuvent utiliser cette application
|
120 |
+
"""
|
121 |
+
|
122 |
+
# Verifier si le nombre de questions demandé correspond au nombre de questions d'un QCM
|
123 |
+
if num_questions not in [5,10,20]:
|
124 |
+
raise MyException(status_code=422, name="num_questions invalide", \
|
125 |
+
message="La requête peut contenir 51,10 ou 20 questions, mais pas "+str(num_questions))
|
126 |
+
|
127 |
+
# Filtrer les questions en fonction des paramètres spécifiés
|
128 |
+
filtered_questions = questions_data
|
129 |
+
if use:
|
130 |
+
filtered_questions = filtered_questions[filtered_questions['use'] == use]
|
131 |
+
if subject is not None:
|
132 |
+
s = subject.split(',')
|
133 |
+
filtered_questions = filtered_questions[filtered_questions['subject'].isin(s)]
|
134 |
+
print("len(filtered_questions)=",len(filtered_questions))
|
135 |
+
print("num_questions=",num_questions)
|
136 |
+
# Vérifier si des questions sont disponibles dans la catégorie spécifiée
|
137 |
+
if len(filtered_questions) == 0:
|
138 |
+
raise MyException(status_code=404, name="Aucune question", \
|
139 |
+
message="Aucune question ne correspond aux critères sélectionnés")
|
140 |
+
# Verifier si le nombre de questions diponibles >= au nombre requis
|
141 |
+
elif (len(filtered_questions) < num_questions):
|
142 |
+
raise MyException(status_code=400, name="Nb insuffisant de questions ", \
|
143 |
+
message="Le nombre de questions correspondantes aux critères sélectionnés est < au nombre requis")
|
144 |
+
|
145 |
+
# Supprimer les valeurs NaN dans les champs correct,responseC, responseD (ils ne sont pas toujours remplis)
|
146 |
+
filtered_questions['correct'] = filtered_questions['correct'].apply(lambda x: None if pd.isna(x) else x)
|
147 |
+
filtered_questions['responseC'] = filtered_questions['responseC'].apply(lambda x: None if pd.isna(x) else x)
|
148 |
+
filtered_questions['responseD'] = filtered_questions['responseD'].apply(lambda x: None if pd.isna(x) else x)
|
149 |
+
|
150 |
+
# Sélectionner un nombre aléatoire de questions
|
151 |
+
selected_questions = filtered_questions.sample(n=min(num_questions, len(filtered_questions)))
|
152 |
+
|
153 |
+
# Convertir les données en liste de dictionnaires
|
154 |
+
questions_list = selected_questions.to_dict(orient='records')
|
155 |
+
|
156 |
+
# Convertir les dictionnaires en objets Pydantic de type Question
|
157 |
+
questions_objects = [Question(**question) for question in questions_list]
|
158 |
+
return questions_objects
|
159 |
+
|
160 |
+
|
161 |
+
# Endpoint pour créer une nouvelle question (accessible uniquement par l'utilisateur admin)
|
162 |
+
@api.post('/questions/create', name="Création d'une nouvelle question")
|
163 |
+
def create_question(question: Question,
|
164 |
+
auth_info: tuple = Depends(authenticate)):
|
165 |
+
"""
|
166 |
+
Crée une nouvelle question et l'ajoute à questions.csv
|
167 |
+
Seuls l' utilisateur admin a le droit d'utiliser cette fonction
|
168 |
+
"""
|
169 |
+
global questions_data
|
170 |
+
|
171 |
+
username, password = auth_info
|
172 |
+
if username != 'admin':
|
173 |
+
raise HTTPException(status_code=401, detail="Utilisateur non authorisé")
|
174 |
+
|
175 |
+
# Ajouter la nouvelle question au DataFrame
|
176 |
+
new_question = question.model_dump()
|
177 |
+
questions_data = questions_data.append(new_question, ignore_index=True)
|
178 |
+
|
179 |
+
# Sauvegarder les modifications dans le fichier CSV
|
180 |
+
questions_data.to_csv('questions.csv', index=False)
|
181 |
+
|
182 |
+
return {'message': 'Question créée avec succès'}
|
183 |
+
|
openapi.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/":{"get":{"summary":"Vérification Que L'Api Fonctionne","operationId":"Vérification_que_l_API_fonctionne__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/questions":{"get":{"summary":"Récupération Des Questions Pour Un Qcm","description":"Récupère les questions en fonction du type de test (use) et des catégories (subject) spécifiés\nL'application peut produire des QCMs de 5, 10 ou 20 questions (seulement)\nLes questions sont retournées dans un ordre aléatoire\nSeuls les utilisateurs se trouvant dans users_credentials peuvent utiliser cette application","operationId":"Récupération_des_questions_pour_un_QCM_questions_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"use","in":"query","required":true,"schema":{"type":"string","title":"Use"}},{"name":"subject","in":"query","required":true,"schema":{"type":"string","title":"Subject"}},{"name":"num_questions","in":"query","required":true,"schema":{"type":"integer","title":"Num Questions"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/questions/create":{"post":{"summary":"Création D'Une Nouvelle Question","description":"Crée une nouvelle question et l'ajoute à questions.csv\nSeuls l' utilisateur admin a le droit d'utiliser cette fonction","operationId":"Création_d_une_nouvelle_question_questions_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Question"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBasic":[]}]}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Question":{"properties":{"question":{"type":"string","title":"Question"},"subject":{"type":"string","title":"Subject"},"correct":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Correct"},"use":{"type":"string","title":"Use"},"responseA":{"type":"string","title":"Responsea"},"responseB":{"type":"string","title":"Responseb"},"responseC":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Responsec"},"responseD":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Responsed"}},"type":"object","required":["question","subject","use","responseA","responseB"],"title":"Question"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBasic":{"type":"http","scheme":"basic"}}}}
|
questions.csv
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
question,subject,use,correct,responseA,responseB,responseC,responseD,remark
|
2 |
+
Que signifie le sigle No-SQL ?,BDD,Test de positionnement,A,Pas seulement SQL,Pas de SQL,Pas tout SQL,,
|
3 |
+
Cassandra et HBase sont des bases de données,BDD,Test de positionnement,C,relationnelles,orientées objet,orientées colonne,orientées graphe,
|
4 |
+
MongoDB et CouchDB sont des bases de données,BDD,Test de positionnement,B,relationnelles,orientées objet,orientées colonne,orientées graphe,
|
5 |
+
OrientDB et Neo4J sont des bases de données,BDD,Test de positionnement,D,relationnelles,orientées objet,orientées colonne,orientées graphe,
|
6 |
+
"Pour indexer des données textuelles, je peux utiliser",BDD,Test de positionnement,A,ElasticSearch,Neo4J,MySQL,,
|
7 |
+
A quoi faire attention lorsqu'on choisit un système de base de données ?,BDD,Test de positionnement,D,La définition d'un schéma,La rapidité de lecture/écriture,La différenciation des accés,Tous ces points,
|
8 |
+
Quels sont les trois éléments constitutifs de Hadoop ?,Systèmes distribués,Test de positionnement,A,"HDFS, YARN et Haddoop MapReduce","Hive, LOL et Spark","Spark, Hadoop MapReduce et Hive",,
|
9 |
+
"Lors de l'étape de Map d'un wordcount appliqué à la phrase ""cette phrase est une phrase"", les valeurs émises sont:",Systèmes distribués,Test de positionnement,B,"1, 2, 1, 1","1, 1, 1, 1, 1, 1","5, 0, 0, 0, 0",,
|
10 |
+
"Dans Hadoop, les combiners permettent",Systèmes distribués,Test de positionnement,C,de combiner les entrées et les sorties,de mieux distribuer la charge lors de la phase de Shuffle,de limiter le nombre de valeurs émises lors de l'étape de Reduce,,
|
11 |
+
"Dans Hadoop, les partitioners permettent",Systèmes distribués,Test de positionnement,B,de combiner les entrées et les sorties,de mieux distribuer la charge lors de la phase de Shuffle,de limiter le nombre de valeurs émises lors de l'étape de Reduce,,
|
12 |
+
Le théorème CAP oppose,Systèmes distribués,Test de positionnement,B,"Capacité, Vitesse, Distribution","Disponibilité, Cohérence, Distribution","Cohérence, Adaptabilité, Puissance",,
|
13 |
+
Hive permet,Systèmes distribués,Test de positionnement,A,d'abstraire une base de données relationnelle,de classer les documents par ordre d'importance,d'orchestrer des clusters de machine,,
|
14 |
+
Spark se différencie de Hadoop par,Systèmes distribués,Test de positionnement,D,son absence de système de stockage,ses nombreuses librairies notamment de Machine Learning,l'écriture en mémoire plutôt que sur disque lors de la phase de Shuffle,Tous ces points,
|
15 |
+
Un système de messagerie asynchrone permet de décorréler les consommateurs et les producteurs,Streaming de données,Test de positionnement,A,Vrai,Faux,,,
|
16 |
+
Kafka est système de messagerie,Streaming de données,Test de positionnement,D,distribué,asynchrone,publication/abonnement,Tous ces points,
|
17 |
+
L'architecture lambda présente les couches,Streaming de données,Test de positionnement,D,batch,temps réel,service,Tous ces points,
|
18 |
+
Docker est utilisé,Docker,Test de positionnement,A,pour développer rapidement et mettre en production facilement,pour améliorer les capacités d'une base de données,pour améliorer la puissance de calcul,,
|
19 |
+
Docker permet de persister des changements,Docker,Test de positionnement,C,Oui,Non,Oui à condition d'utiliser des volumes,,
|
20 |
+
Des containers Docker peuvent communiquer entre eux grâce à,Docker,Test de positionnement,B,des volumes,des networks,des communications ,,
|
21 |
+
DockerHub est,Docker,Test de positionnement,C,un système qui permet de lancer plusieurs containers d'un coup,un système d'orchestration de containers,un répertoire d'images Docker,,
|
22 |
+
Docker-compose est ,Docker,Test de positionnement,A,un système qui permet de lancer plusieurs containers d'un coup,un système d'orchestration de containers,un répertoire d'images Docker,,
|
23 |
+
Lequel de ces problème est un problème de classification:,Classification,Test de validation,C,Segmentation clients,Calcul de prix optimal,Prédiction du caractère bénin d’une tumeur,,
|
24 |
+
Lequel de ces problème est un problème de classification,Classification,Test de validation,B,Estimation du prix d’une oeuvre d’art,Prédiction du départ d’un client,Modélisation des flux d’air autour d’un réacteur,,
|
25 |
+
Lequel de ces problème est un problème de classification,Classification,Test de validation,"B,C",Labellisation d’une image,Reconnaissance d’objet,Génération automatique de mots,,
|
26 |
+
Est-ce que les algorithmes de classification permettent de donner une probabilité d’appartenance à une classe plutôt que simplement l’étiquette?,Classification,Test de validation,A,"Oui, c’est en général la base de tous les algorithmes de classification","Non, ce problème est un problème de régression",,,
|
27 |
+
Quelle métrique est utilisée en classification ?,Classification,Test de validation,A,Le F1-score,Le RMSE,La perplexité,,
|
28 |
+
Quel algorithme est mieux adapté à un jeu de données majoritairement composé de variables qualitatives?,Classification,Test de validation,C,SVM,Régression Logistique,Arbre de décision,,
|
29 |
+
"Dans un problème de fraude bancaire, la précision est-elle une métrique adaptée?
|
30 |
+
",Classification,Test de validation,B,Oui puisque c’est un problème de classification.,Non car le déséquilibre des deux classes ne permet pas une amélioration significative de cette métrique,,,
|
31 |
+
De donner une meilleure métrique dans toutes les situations,Classification,Test de validation,C,De donner une meilleure métrique dans toutes les situations,D’être plus facilement interprétable ,D’identifier facilement des proportions d’observations en fonction de leur probabilité,,
|
32 |
+
"La spécificité est définie par (V: vrai, F: faux, P: positif, N: négatif) : ",Classification,Test de validation,B,(VP + VN) / (VP + VN + FN + FP),VN / (VN + FP),VP / (VP + FN),,
|
33 |
+
"La sensibilité est définie par (V: vrai, F: faux, P: positif, N: négatif) : ",Classification,Test de validation,C,(VP + VN) / (VP + VN + FN + FP),VN / (VN + FP),VP / (VP + FN),,
|
34 |
+
Hadoop permet de: ,Systèmes distribués,Test de validation,D,Stocker des données,Faire des calculs,Orchestrer des jobs MapReduce,Tous ces points,
|
35 |
+
"Dans le paradigme MapReduce, les combiners servent à: ",Systèmes distribués,Test de validation,B,Équilibrer la charge de travail des reducers,Diminuer le nombre de valeurs à émettre des mappers aux reducers,Accélérer les calculs,Tous ces points,
|
36 |
+
"Dans le paradigme MapReduce, les partitioners servent à: ",Systèmes distribués,Test de validation,A,Équilibrer la charge de travail des reducers,Diminuer le nombre de valeurs à émettre des mappers aux reducers,Accélérer les calculs,Tous ces points,
|
37 |
+
L'utilisation de systèmes distrubués permet,Systèmes distribués,Test de validation,D,D'accélérer les calculs ,De sécuriser l'accés aux données,D'augmenter facilement la capacité de stockage,Tous ces points,
|
38 |
+
Hadoop streaming est un outil qui permet,Systèmes distribués,Test de validation,A,De faire des jobs MapReduce avec n'importe quel langage de programmation,De faire du traitement de données en temps réel ,De stocker plusieurs copies d'un jeu de données,De stocker des vidéos sur un système distribué,
|
39 |
+
"Dans le cas d'une architecture distribuée, le théorème CAP nous oblige à choisir entre:",Systèmes distribués,Test de validation,A,Cohérence et Disponibilité,Partition et Cohérence,Partition et Disponibilité,,
|
40 |
+
Hive est ,Sytèmes distribués,Test de validation,B,Un système de gestion de bases de données relationnelles,Une abstraction de SGDB relationnelles,Une librairie de Hadoop,,
|
41 |
+
"Pour Hive, partitioner consiste à ",Systèmes distribués,Test de validation,C,Découper les tables en block de taille pré-définie,Découper les tables par colonnes,Découper les tables selon les valeurs d'une variable,,
|
42 |
+
Pig permet,Systèmes distribués,Test de validation,A,De rendre l'écriture de jobs MapReduce plus simple et plus intuitive,D'accélérer les calculs effectués sur Hadoop ,D'orchestrer Hive,Tous ces points,
|
43 |
+
Sqoop est utilisé,Systèmes distribués,Test de validation,A,Pour transférer des données depuis des SGDBR vers HDFS et inversement,De faire des calculs en temps réel ,D'automatiser des jobs Map Reduce,Tous ces points,
|
44 |
+
Data science is ...,Data Science,Total Bootcamp,A,A set of techniques and tools used to get value out of data.,A scientific approach of data acquisition.,A set of empirical approachs used to define theoretical formulas and /or equations thanks to data.,,
|
45 |
+
Its applications are ...,Data Science,Total Bootcamp,A,Limited to a small amount of fields and use cases.,Close to unlimited and find use cases in almost every known fields where data can be collected.,,,
|
46 |
+
What are the first things you want to do when you start a Data Science project ?,Data Science,Total Bootcamp,A C,Define the problem.,Choose the model you want to implement.,Obtain the data and check if it fits our standard.,Ask Paul what to do next.,
|
47 |
+
Are every datasets worth a Data Science project ?,Data Science,Total Bootcamp,A,No.,"If it's big enough, yes.",Yes.,,
|
48 |
+
"When the dataset is all set and obtained, what do you need to do ?",Data Science,Total Bootcamp,B C,Run a model on it and then do a series of statistical tests on it.,Explore it and do a series of statistical tests on it.,Pre-process it by cleaning it of missing values or irrelevant data.,,
|
49 |
+
What are the best tools you can use when starting a project?,Data Science,Total Bootcamp,A B C D ,Data Visualization.,Statistical tests.,Expert intuitions. ,Correlation matrix ,Gut feelings.
|
50 |
+
"When building a model, you have to",Data Science,Total Bootcamp,B,Look out for parameters that can be optimized and optimze them.,Train it on all the data available.,,,
|
51 |
+
"Your model is all done and working, what's next?",Data Science,Total Bootcamp,B,My project is done ! ,Analyze the results and tune the existing model to fit best the problem defined initially.,,,
|
52 |
+
What is Machine learning ?,Machine Learning,Total Bootcamp,B,The phenomenon in which an algorithm realizes it is not paid enough and puts itself on a strike.,An application of artificial intelligence that provides systems the ability to automatically learn and improve from experience without being explicitly programmed.,Exactly what Datascientest offers : a way to learn through machines.,When an algorithm is programmed to adapt itself to a given situation.,
|
53 |
+
Supervised learning ...,Machine Learning,Total Bootcamp,A C,Is when the data we use to fit the model on is labeled.,Is when the algorithm includes steps to check the progression of the computation process.,Allows to predict the value or the class of a new element.,"Allows data partitioning according to the features,distribution density estimation and dimension reduction.",
|
54 |
+
Unsupervised learning ...,Machine Learning,Total Bootcamp,A C ,Is when the data we feed to our model is not labeled.,Allows to predict the value or the class of a new element.,"Allows data partitioning according to the features,distribution density estimation and dimension reduction.",,
|
55 |
+
Classification,Machine Learning,Total Bootcamp,B,Is used when the target we aim to predict is continuous.,Is used when the target we aim to work on is discrete.,Is not a method needing artificial intelligence techniques.,,
|
56 |
+
Regression,Machine Learning,Total Bootcamp,A,Is used when the target we aim to predict is continuous.,Is used when the target we aim to predict is discrete.,Gives the same results with two points or a thousand.,,
|
57 |
+
Overfitting is,Machine Learning,Total Bootcamp,A,When the model fits too much the training data and don’t generalize enough.,When the model takes too much time to train on the data.,When the algorithm can't store anymore the results of the fitting process.,,
|
58 |
+
A way to handle imbalanced datasets is,Machine Learning,Total Bootcamp,"B,C",Filtering,Under sampling,Over sampling,,
|
59 |
+
Pourquoi utiliser des APIs?,Automation,Test de validation,D,Pour isoler les services de l'utilisateur final,Pour normaliser les communications entre services,Pour permettre une évolution facile des services,Tous ces points,
|
60 |
+
"En utilisant Flask, on permet à un utilisateur ou à une machine d'utiliser un service avec ",Automation,Test de validation,C,SQL,Python,HTTP,Java,
|
61 |
+
Pourquoi utiliser Docker?,Automation,Test de validation,D,Pour déployer facilement et rapidement des processus,Pour isoler des processus de la machine hôte,Pour tester des services rapidement,Tous ces points,
|
62 |
+
"Si on fait un parallèle avec la programmation orientée objet, les images de containers Docker sont l'équivalent des",Automation,Test de validation,A,Classes,Instances de classes,Méthodes,Objets,
|
63 |
+
"Pour faciliter le passage de fichiers entre la machine hôte et un container Docker, on peut utiliser les",Automation,Test de validation,C,Images,Networks,Volumes,Containers,
|
64 |
+
"Pour faciliter la communication entre la machine hôte et un container Docker, on peut utiliser les",Automation,Test de validation,B,Images,Networks,Volumes,Containers,
|
65 |
+
Quelle commande permet de lancer un container Docker ?,Automation,Test de validation,B,docker image run nom_de_l_image,docker container run nom_de_l_image,docker image pull nom_de_l_image,docker container pull nom_de_l_image,
|
66 |
+
A quoi sert Docker-Compose ?,Automation,Test de validation,A,A répertorier les images publiques de containers,A déployer plusieurs containers en même temps,A créer sa propre image Docker,Tous ces points,
|
67 |
+
A quoi sert Docker-Compose ?,Automation,Test de validation,B,A répertorier les images publiques de containers,A déployer plusieurs containers en même temps,A créer sa propre image Docker,Tous ces points,
|
68 |
+
"Dans Airflow, le DAG répertorie",Automation,Test de validation,D,Les tâches à effectuer,Les actions à prendre en cas d'échecs,L'enchaînement des tâches à effectuer,Tous ces points,
|
69 |
+
Quelle est la différence entre le processing time et le event time ?,Streaming de données,Test de validation,B,L’un correspond au temps de début de calcul alors que l’autre correspond à la durée nécessaire pour le calcul,L’un correspond à l’entrée de la donnée dans le système alors que l’autre correspond à la date de création de la donnée,Ce sont en fait la même chose,,
|
70 |
+
Kafka est un système de messagerie,Streaming de données,Test de validation,A,Publication/Abonnement + Asynchrone,Publication/Abonnement + Synchrone,Orienté queue + Asynchrone,Orienté queue+ Synchrone,
|
71 |
+
"Dans Kafka, les brokers permettent de",Streaming de données,Test de validation,,distribuer le stockage intermédiaire des données,répartir la charge du flux des données,prévenir les pannes du système,tous ces points,
|
72 |
+
"Dans Kafka, dans un consumer group, les consommateurs accédent",Streaming de données,Test de validation,,chacun à toutes les données disponibles,chacun à une partie des données relativement à sa capacité de consommation,"chacun à une partie des données, réparti de manière uniforme",,
|
73 |
+
Quelle est la différence entre Hadoop et Spark?,Streaming de données,Test de validation,,Hadoop est un système de calcul et de stockage alors que Spark n’est qu’un système de calcul,Spark est écrit en Scala alors que Hadoop est écrit en Java,Spark écrit les valeurs en mémoire alors que Hadoop les écrit sur disque,Tous ces points,
|
74 |
+
Quelle librairie de Spark n’existe pas ?,Streaming de données,Test de validation,,SparkSQL,SparkML,Spark Streaming,Spark IO,
|
75 |
+
Que signigie RDD ?,Streaming de données,Test de validation,,Raw distributed dataset,Redundant Distributed Dataset,Resilient Distributed DataSet,,
|
76 |
+
Qu’est-ce que le DAG ?,Streaming de données,Test de validation,,Une représentation des tâches à exécuter,Un dispositif qui permet d’optimiser les claculs,,,
|
77 |
+
Les Dstreams sont définis par,Streaming de données,Test de validation,,Une limite de temps,Une limite d’espace,Une limite déterminée aléatoirement,Tous ces points,
|
78 |
+
"Dans HBase, les données sont stockées par",Streaming de données,Test de validation,,Lignes,Familles de colonnes,Clefs,,
|
requirements.txt
CHANGED
@@ -1,4 +1,21 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
annotated-types==0.6.0
|
2 |
+
anyio==4.2.0
|
3 |
+
click==8.1.7
|
4 |
+
exceptiongroup==1.2.0
|
5 |
+
fastapi==0.109.2
|
6 |
+
h11==0.14.0
|
7 |
+
httptools==0.1.2
|
8 |
+
idna==3.6
|
9 |
+
numpy==1.24.4
|
10 |
+
pandas==1.5.3
|
11 |
+
pydantic==2.6.1
|
12 |
+
pydantic-core==2.16.2
|
13 |
+
python-dateutil==2.8.2
|
14 |
+
pytz==2024.1
|
15 |
+
requests==2.7.0
|
16 |
+
six==1.16.0
|
17 |
+
sniffio==1.3.0
|
18 |
+
starlette==0.36.3
|
19 |
+
typing-extensions==4.9.0
|
20 |
+
uvicorn==0.27.1
|
21 |
+
uvloop==0.19.0
|