From 2ce5b1bee0a37f054511165998cf9ea38cad2e0a Mon Sep 17 00:00:00 2001 From: Angel Dijoux <77701490+Angel-Dijoux@users.noreply.github.com> Date: Tue, 31 Oct 2023 01:21:04 +0100 Subject: [PATCH] [HomeFormation] Create new endpoint for homepage. (#13) * feat: add endpoint and quick fix on size. * fix: ci --- .../1932d4fa450b_alter_table_formation.py | 40 +++++++ ...5a52b_alter_formation_table_update_size.py | 104 ++++++++++++++++++ src/blueprints/favoris.py | 2 +- src/blueprints/formations.py | 20 +++- src/blueprints/route_handler.py | 2 +- .../formation/scrap/get_formation.py | 32 ++++-- src/models/formation.py | 14 ++- 7 files changed, 192 insertions(+), 22 deletions(-) create mode 100644 migrations/versions/1932d4fa450b_alter_table_formation.py create mode 100644 migrations/versions/3d2e4be5a52b_alter_formation_table_update_size.py diff --git a/migrations/versions/1932d4fa450b_alter_table_formation.py b/migrations/versions/1932d4fa450b_alter_table_formation.py new file mode 100644 index 0000000..2307a2b --- /dev/null +++ b/migrations/versions/1932d4fa450b_alter_table_formation.py @@ -0,0 +1,40 @@ +"""Update size for 'demain' again. + +Revision ID: 1932d4fa450b +Revises: 3d2e4be5a52b +Create Date: 2023-10-31 01:10:05.699656 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = "1932d4fa450b" +down_revision = "3d2e4be5a52b" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "formation", + "domain", + existing_type=mysql.VARCHAR(length=255), + type_=sa.Text(), + existing_nullable=False, + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "formation", + "domain", + existing_type=sa.Text(), + type_=mysql.VARCHAR(length=255), + existing_nullable=False, + ) + # ### end Alembic commands ### diff --git a/migrations/versions/3d2e4be5a52b_alter_formation_table_update_size.py b/migrations/versions/3d2e4be5a52b_alter_formation_table_update_size.py new file mode 100644 index 0000000..5e9c73b --- /dev/null +++ b/migrations/versions/3d2e4be5a52b_alter_formation_table_update_size.py @@ -0,0 +1,104 @@ +"""Alter table formation upate size. + +Revision ID: 3d2e4be5a52b +Revises: ee56a7eaab4a +Create Date: 2023-10-31 00:49:13.312551 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = "3d2e4be5a52b" +down_revision = "ee56a7eaab4a" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "formation", + "type", + existing_type=mysql.VARCHAR(length=120), + type_=sa.String(length=255), + existing_nullable=False, + ) + op.alter_column( + "formation", + "libelle", + existing_type=mysql.VARCHAR(length=120), + type_=sa.String(length=255), + existing_nullable=False, + ) + op.alter_column( + "formation", + "tutelle", + existing_type=mysql.VARCHAR(length=120), + type_=sa.String(length=255), + existing_nullable=False, + ) + op.alter_column( + "formation", + "niveau_de_sortie", + existing_type=mysql.VARCHAR(length=120), + type_=sa.String(length=255), + existing_nullable=False, + ) + op.alter_column( + "formation", + "duree", + existing_type=mysql.VARCHAR(length=15), + type_=sa.String(length=255), + existing_nullable=False, + ) + op.drop_constraint("favori_formation_id_fk", "user_favori", type_="foreignkey") + op.create_foreign_key( + None, "user_favori", "formation", ["formation_id"], ["id"], ondelete="CASCADE" + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, "user_favori", type_="foreignkey") + op.create_foreign_key( + "favori_formation_id_fk", "user_favori", "formation", ["formation_id"], ["id"] + ) + op.alter_column( + "formation", + "duree", + existing_type=sa.String(length=255), + type_=mysql.VARCHAR(length=15), + existing_nullable=False, + ) + op.alter_column( + "formation", + "niveau_de_sortie", + existing_type=sa.String(length=255), + type_=mysql.VARCHAR(length=120), + existing_nullable=False, + ) + op.alter_column( + "formation", + "tutelle", + existing_type=sa.String(length=255), + type_=mysql.VARCHAR(length=120), + existing_nullable=False, + ) + op.alter_column( + "formation", + "libelle", + existing_type=sa.String(length=255), + type_=mysql.VARCHAR(length=120), + existing_nullable=False, + ) + op.alter_column( + "formation", + "type", + existing_type=sa.String(length=255), + type_=mysql.VARCHAR(length=120), + existing_nullable=False, + ) + # ### end Alembic commands ### diff --git a/src/blueprints/favoris.py b/src/blueprints/favoris.py index 6e96ad3..9c208a2 100644 --- a/src/blueprints/favoris.py +++ b/src/blueprints/favoris.py @@ -62,7 +62,7 @@ def post_favori_by_user_id() -> Tuple[Response, int] | HTTPException: db.session.add(favori) db.session.commit() - return jsonify(favori), HTTP_201_CREATED + return jsonify(favori.to_dict()), HTTP_201_CREATED @favoris.route("/", methods=["GET"]) diff --git a/src/blueprints/formations.py b/src/blueprints/formations.py index 777e45e..d3341d8 100644 --- a/src/blueprints/formations.py +++ b/src/blueprints/formations.py @@ -7,6 +7,7 @@ from src.blueprints.route_handler import HttpMethod, route_handler from src.business_logic.formation.scrap.get_formation import ( get_libelle_type_formation, + get_main_formations, search_formations, ) from src.constants.http_status_codes import ( @@ -21,13 +22,22 @@ def _filter_by_link(formations: list[dict[str, Any]], for_id: str) -> dict[str, return filtered_list[0] if filtered_list else {} +@route_handler(formations, "/", HttpMethod.POST) +def resolve_get_main_formations() -> Tuple[Response, int] | HTTPException: + data = request.get_json() + offset = data.get("offset") + limit = data.get("limit") + + return get_main_formations(limit, offset) + + @route_handler( formations, "/", HttpMethod.GET, "../docs/formations/formation.yaml", ) -def get_formation_by_id(id: str) -> Tuple[Response, int] | HTTPException: +def resolve_get_formation_by_id(id: str) -> Tuple[Response, int] | HTTPException: with open("assets/formation/data.json", "r") as json_file: result = _filter_by_link(json.load(json_file)["formations"]["formation"], id) return result, HTTP_200_OK if len(result) > 0 else HTTP_200_OK @@ -39,7 +49,7 @@ def get_formation_by_id(id: str) -> Tuple[Response, int] | HTTPException: HttpMethod.POST, "../docs/formations/searchFormation.yaml", ) -def get_search_formation() -> Tuple[Response, int] | HTTPException: +def resolve_get_search_formation() -> Tuple[Response, int] | HTTPException: post = request.get_json() query = post.get("query") limit = post.get("limit") @@ -54,8 +64,8 @@ def get_search_formation() -> Tuple[Response, int] | HTTPException: HttpMethod.POST, "../docs/formations/formationByLibelle.yaml", ) -def get_formation_by_libelle() -> Tuple[Response, int] | HTTPException: - post = request.get_json() - query = post.get("query") +def resolve_get_formation_by_libelle() -> Tuple[Response, int] | HTTPException: + data = request.get_json() + query = data.get("query") return get_libelle_type_formation(query) diff --git a/src/blueprints/route_handler.py b/src/blueprints/route_handler.py index 6c1140e..cc9df3b 100644 --- a/src/blueprints/route_handler.py +++ b/src/blueprints/route_handler.py @@ -20,7 +20,7 @@ def route_handler( blueprint: Blueprint, route: str, method: HttpMethod, swag_yaml: str = None ) -> Callable: def decorator(func: Callable) -> Callable: - @ swag_from(swag_yaml) if swag_yaml else lambda: None + @swag_from(swag_yaml if swag_yaml else lambda: None) def inner_wrapper(*args, **kwargs): try: return jsonify(func(*args, **kwargs)), HTTP_200_OK diff --git a/src/business_logic/formation/scrap/get_formation.py b/src/business_logic/formation/scrap/get_formation.py index a941f9e..e62a2e4 100644 --- a/src/business_logic/formation/scrap/get_formation.py +++ b/src/business_logic/formation/scrap/get_formation.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass import requests from src.business_logic.formation import ONISEP_URL from src.business_logic.formation.scrap.types import ( @@ -19,13 +18,8 @@ def _get_data(params: str) -> dict: return response.json() -def search_formations(query: str, limit: int, offset: int = None) -> SearchedFormations: - params = f"/search?q={query}&size={limit}" - if offset: - params += f"&from={offset}" - data = _get_data(params) - - formated_formations = [ +def _format_formations(data: list[dict]) -> list[Formation]: + return [ Formation( int(formation["code_nsf"] or 0), formation["sigle_type_formation"] or formation["libelle_type_formation"], @@ -36,9 +30,18 @@ def search_formations(query: str, limit: int, offset: int = None) -> SearchedFor formation["niveau_de_sortie_indicatif"], formation["duree"], ) - for formation in data["results"] + for formation in data ] + +def search_formations(query: str, limit: int, offset: int = None) -> SearchedFormations: + params = f"/search?q={query}&size={limit}" + if offset: + params += f"&from={offset}" + data = _get_data(params) + + formated_formations = _format_formations(data["results"]) + return SearchedFormations(data["total"], formated_formations) @@ -46,3 +49,14 @@ def get_libelle_type_formation(query: str) -> list[Facet]: params = f"/search?q={query}" data = _get_data(params) return data["facets"]["libelle_type_formation"] + + +def get_main_formations(limit: int = 10, offset: int = None) -> SearchedFormations: + params = f"/search?&size={limit}" + if offset: + params += f"&from={offset}" + data = _get_data(params) + + formated_formations = _format_formations(data["results"]) + + return SearchedFormations(data["total"], formated_formations) diff --git a/src/models/formation.py b/src/models/formation.py index 508a237..93ab532 100644 --- a/src/models/formation.py +++ b/src/models/formation.py @@ -1,5 +1,7 @@ import uuid +from sqlalchemy import Text + from src import db from src.models.base_model import BaseModel from src.models.helpers.UUIDType import UUIDType @@ -20,10 +22,10 @@ class Formation(BaseModel): primary_key=True, ) code_nsf = db.Column(db.Integer, nullable=False) - type = db.Column(db.String(120), nullable=False) - libelle = db.Column(db.String(120), nullable=False) - tutelle = db.Column(db.String(120), nullable=False) + type = db.Column(db.String(255), nullable=False) + libelle = db.Column(db.String(255), nullable=False) + tutelle = db.Column(db.String(255), nullable=False) url = db.Column(db.String(255), nullable=False, unique=True) - domain = db.Column(db.String(255), nullable=False) - niveau_de_sortie = db.Column(db.String(120), nullable=False) - duree = db.Column(db.String(15), nullable=False) + domain = db.Column(Text, nullable=False) + niveau_de_sortie = db.Column(db.String(255), nullable=False) + duree = db.Column(db.String(255), nullable=False)