From 82e27eb3fb1fd2143d564801b564dde4dcc24e4c Mon Sep 17 00:00:00 2001 From: AntoinePELAMOURGUES Date: Fri, 22 Nov 2024 00:04:48 +0400 Subject: [PATCH] :white_check_mark: Update tests --- api/auth.py | 43 +++++++++++++++++++++++++++++++++++++++++-- api/predict.py | 43 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/api/auth.py b/api/auth.py index 2968132..64fff82 100644 --- a/api/auth.py +++ b/api/auth.py @@ -56,30 +56,56 @@ class Token(BaseModel): name='user_creation_requests_total', documentation='Total number of user creation requests', labelnames=['status_code'], + registry=collector ) login_requests_counter = Counter( name='login_requests_total', documentation='Total number of login requests', labelnames=['status_code'], + registry=collector ) user_creation_duration_histogram = Histogram( name='user_creation_duration_seconds', documentation='Duration of user creation requests in seconds', labelnames=['status_code'], + registry=collector ) login_duration_histogram = Histogram( name='login_duration_seconds', documentation='Duration of login requests in seconds', labelnames=['status_code'], + registry=collector ) error_counter = Counter( name='user_creation_errors_total', documentation='Total number of user creation errors', labelnames=['error_type'], + registry=collector +) + +password_validation_counter = Counter( + name='password_validation_errors_total', + documentation='Total number of password validation errors', + labelnames=['error_type'], + registry=collector +) + +email_validation_counter = Counter( + name='email_validation_errors_total', + documentation='Total number of email validation errors', + labelnames=['error_type'], + registry=collector +) + +username_validation_counter = Counter( + name='username_validation_errors_total', + documentation='Total number of username validation errors', + labelnames=['error_type'], + registry=collector ) # Fonction pour valider le nom d'utilisateur @@ -117,17 +143,23 @@ async def create_user(create_user_request: CreateUserRequest): # Validation checks username_error = validate_username(create_user_request.username) if username_error: + username_validation_counter.labels(error_type='invalid_username').inc() error_counter.labels(error_type='invalid_username').inc() + logger.error(f"Erreur validation nom d'utilisateur: {username_error}") raise HTTPException(status_code=400, detail=username_error) mail_error = validate_email(create_user_request.email) if mail_error: + email_validation_counter.labels(error_type='invalid_mail').inc() error_counter.labels(error_type='invalid_mail').inc() + logger.error(f"Erreur validation email: {mail_error}") raise HTTPException(status_code=400, detail=mail_error) password_error = validate_password(create_user_request.password) if password_error: + password_validation_counter.labels(error_type='invalid_password').inc() error_counter.labels(error_type='invalid_password').inc() + logger.error(f"Erreur validation mot de passe: {password_error}") raise HTTPException(status_code=400, detail=password_error) try: @@ -137,22 +169,24 @@ async def create_user(create_user_request: CreateUserRequest): if cur.fetchone() is not None: error_counter.labels(error_type='username_already_registered').inc() user_creation_counter.labels(status_code='400').inc() + logger.error("Email déjà enregistré") raise HTTPException(status_code=400, detail="Email already registered") # Créer le nouvel utilisateur hached_password = bcrypt_context.hash(create_user_request.password) cur.execute("INSERT INTO users (username, email, hached_password) VALUES (%s, %s, %s)", (create_user_request.username, create_user_request.email, hached_password,)) conn.commit() - print("transaction validée") + logger.info("Utilisateur créé avec succès") except psycopg2.Error as e: error_counter.labels(error_type='database_error').inc() + logger.error(f"Erreur base de données: {str(e)}") raise HTTPException(status_code=500, detail=f"Database error: {str(e)}") duration = time.time() - start_time user_creation_duration_histogram.labels(status_code='201').observe(duration) user_creation_counter.labels(status_code='201').inc() - + logger.info(f"Durée de création utilisateur: {duration} secondes") # Route pour obtenir un token d'accès @router.post("/token", response_model=Token) @@ -163,12 +197,14 @@ async def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, logger.info(f"Retour fonction authenticate_user: {user}") if not user: login_requests_counter.labels(status_code='401').inc() + logger.error("Échec de l'authentification: utilisateur non valide") raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Could not validate user.') token = create_access_token(user['email'], user['userId'], timedelta(minutes=30)) duration = time.time() - start_time login_duration_histogram.labels(status_code='200').observe(duration) login_requests_counter.labels(status_code='200').inc() + logger.info(f"Durée de connexion: {duration} secondes") # Modification de la structure de réponse pour garantir la cohérence response = { @@ -193,10 +229,12 @@ async def authenticate_user(email: str, password: str): if not user: login_requests_counter.labels(status_code='404').inc() + logger.error("Utilisateur non trouvé") raise HTTPException(status_code=404, detail="Utilisateur non trouvé") if not bcrypt_context.verify(password, user[3]): login_requests_counter.labels(status_code='401').inc() + logger.error("Mot de passe incorrect") raise HTTPException(status_code=401, detail="Mot de passe incorrect") # Modification de la structure retournée pour garantir la cohérence @@ -211,6 +249,7 @@ async def authenticate_user(email: str, password: str): except psycopg2.Error as e: error_counter.labels(error_type='database_error').inc() + logger.error(f"Erreur base de données: {str(e)}") raise HTTPException(status_code=500, detail=f"Database error: {str(e)}") diff --git a/api/predict.py b/api/predict.py index 871cfd8..581a21f 100644 --- a/api/predict.py +++ b/api/predict.py @@ -245,6 +245,18 @@ def movie_finder(title): documentation='Count of API errors by type', labelnames=['error_type'], registry=collector) +# Nombre de films recommandés +recommendations_counter = Counter( + name='number_of_recommendations', + documentation='Number of movie recommendations made', + labelnames=['endpoint'], + registry=collector) +# Temps de traitement des requêtes TMDB +tmdb_request_duration_histogram = Histogram( + name='tmdb_request_duration_seconds', + documentation='Duration of TMDB API requests', + labelnames=['endpoint'], + registry=collector) # --------------------------------------------------------------- @@ -318,7 +330,11 @@ async def predict(user_request: UserRequest) -> Dict[str, Any]: df_user = df_user.sort_values(by='rating', ascending=False) best_movies = df_user.head(3) imdb_list = [imdb_dict[movie_id] for movie_id in best_movies['movieId'] if movie_id in imdb_dict] + start_tmdb_time = time.time() results = api_tmdb_request(imdb_list) + tmdb_duration = time.time() - start_tmdb_time + tmdb_request_duration_histogram.labels(endpoint='/predict/best_user_movies').observe(tmdb_duration) + recommendations_counter.labels(endpoint='/predict/best_user_movies').inc(len(results)) # Mesurer la taille de la réponse et l'enregistrer response_size = len(json.dumps(results)) # Calculer la durée et enregistrer dans l'histogramme @@ -329,10 +345,13 @@ async def predict(user_request: UserRequest) -> Dict[str, Any]: response_size_histogram.labels(method='POST', endpoint='/predict/best_user_movies').observe(response_size) # Enregistrer la taille de la réponse # Utiliser le logger pour voir les résultats logger.info(f"Api response: {results}") + logger.info(f"Durée de la requête: {duration} secondes") + logger.info(f"Taille de la réponse: {response_size} octets") return results except ValueError as e: status_code_counter.labels(status_code="400").inc() # Compter les réponses échouées error_counter.labels(error_type="ValueError").inc() # Enregistrer l'erreur spécifique + logger.error(f"Erreur de conversion de l'ID utilisateur: {e}") raise HTTPException(status_code=400, detail="L'ID utilisateur doit être un nombre entier") @@ -363,9 +382,11 @@ async def predict(user_request: UserRequest) -> Dict[str, Any]: recommendations = get_user_recommendations(user_id, model_svd, ratings, n_recommendations = 8) logger.info(f"Recommandations pour l'utilisateur {userId}: {recommendations}") imdb_list = [imdb_dict[movie_id] for movie_id in recommendations if movie_id in imdb_dict] + start_tmdb_time = time.time() results = api_tmdb_request(imdb_list) - - + tmdb_duration = time.time() - start_tmdb_time + tmdb_request_duration_histogram.labels(endpoint='/predict/identified_user').observe(tmdb_duration) + recommendations_counter.labels(endpoint='/predict/identified_user').inc(len(results)) # Mesurer la taille de la réponse et l'enregistrer response_size = len(json.dumps(results)) # Calculer la durée et enregistrer dans l'histogramme @@ -376,11 +397,14 @@ async def predict(user_request: UserRequest) -> Dict[str, Any]: response_size_histogram.labels(method='POST', endpoint='/predict/identified_user').observe(response_size) # Enregistrer la taille de la réponse # Utiliser le logger pour voir les résultats logger.info(f"Api response: {results}") + logger.info(f"Durée de la requête: {duration} secondes") + logger.info(f"Taille de la réponse: {response_size} octets") return results except ValueError as e: status_code_counter.labels(status_code="400").inc() # Compter les réponses échouées error_counter.labels(error_type="ValueError").inc() # Enregistrer l'erreur spécifique + logger.error(f"Erreur de conversion de l'ID utilisateur: {e}") raise HTTPException(status_code=400, detail="L'ID utilisateur doit être un nombre entier") @@ -404,17 +428,26 @@ async def predict(user_request: UserRequest) -> Dict[str, Any]: # Récupérer les ID des films recommandés en utilisant la fonction de similarité recommendations = get_movie_title_recommendations(model_Knn, movie_id, X, movie_mapper, movie_inv_mapper, 9) imdb_list = [imdb_dict[movie_id] for movie_id in recommendations if movie_id in imdb_dict] + start_tmdb_time = time.time() results = api_tmdb_request(imdb_list) - + tmdb_duration = time.time() - start_tmdb_time + tmdb_request_duration_histogram.labels(endpoint='/predict/similar_movies').observe(tmdb_duration) + recommendations_counter.labels(endpoint='/predict/similar_movies').inc(len(results)) # Mesurer la taille de la réponse et l'enregistrer response_size = len(json.dumps(results)) # Calculer la durée et enregistrer dans l'histogramme duration = time.time() - start_time - # Enregistrement des métriques pour Prometheus + # Enregistrement des m��triques pour Prometheus status_code_counter.labels(status_code="200").inc() # Compter les réponses réussies duration_of_requests_histogram.labels(method='POST', endpoint='/predict/similar_movies', user_id="N/A").observe(duration) # Enregistrer la durée de la requête response_size_histogram.labels(method='POST', endpoint='/predict/similar_movies').observe(response_size) # Enregistrer la taille de la réponse logger.info(f"Api response: {results}") - response_size_histogram.labels(method='POST', endpoint='/identified_user').observe(response_size) # Enregistrer la taille de la réponse + logger.info(f"Durée de la requête: {duration} secondes") + logger.info(f"Taille de la réponse: {response_size} octets") return results + except Exception as e: + status_code_counter.labels(status_code="500").inc() # Compter les réponses échouées + error_counter.labels(error_type="Exception").inc() # Enregistrer l'erreur spécifique + logger.error(f"Erreur lors du traitement de la requête: {e}") + raise HTTPException(status_code=500, detail="Erreur interne du serveur")