diff --git a/src/backend/app/db/db_models.py b/src/backend/app/db/db_models.py index 1f669f8b..8b4a2ac7 100644 --- a/src/backend/app/db/db_models.py +++ b/src/backend/app/db/db_models.py @@ -310,3 +310,19 @@ class DbUserProfile(Base): certificate_url = cast(str, Column(String, nullable=True)) # drone registration certificate registration_certificate_url = cast(str, Column(String, nullable=True)) + + +class DbDroneFlightHeight(Base): + """Describes drone altitude regulations by country.""" + + __tablename__ = "drone_flight_height" + + id = cast(int, Column(Integer, primary_key=True, autoincrement=True)) + country = cast(str, Column(String, nullable=False, unique=True)) + country_code = cast( + str, Column(String(3), nullable=False) + ) # ISO 3166-1 alpha-3 country code + max_altitude_ft = cast(float, Column(Float, nullable=False)) + max_altitude_m = cast(float, Column(Float, nullable=False)) + created_at = cast(datetime, Column(DateTime, default=timestamp)) + updated_at = cast(datetime, Column(DateTime, nullable=True)) diff --git a/src/backend/app/drones/drone_routes.py b/src/backend/app/drones/drone_routes.py index 7c9b6552..e4411e25 100644 --- a/src/backend/app/drones/drone_routes.py +++ b/src/backend/app/drones/drone_routes.py @@ -80,3 +80,35 @@ async def read_drone( dict: The drone record if found. """ return drone + + +@router.get("/drone-altitude/") +async def get_all_altitudes( + db: Annotated[Connection, Depends(database.get_db)], + user_data: Annotated[AuthUser, Depends(login_required)], +): + """ + Retrieves all drone altitude regulations. + """ + altitudes = await drone_schemas.DroneFlightHeight.all(db) + if not altitudes: + return [] + return altitudes + + +@router.get("/drone-altitude/{country}/") +async def get_drone_altitude_by_country( + country: str, + db: Annotated[Connection, Depends(database.get_db)], + user_data: Annotated[AuthUser, Depends(login_required)], +): + """ + Get drone altitude details by country. + + """ + result = await drone_schemas.DroneFlightHeight.one(db, country) + + if not result: + return [] + + return result diff --git a/src/backend/app/drones/drone_schemas.py b/src/backend/app/drones/drone_schemas.py index dfc3d1b9..10fe0d54 100644 --- a/src/backend/app/drones/drone_schemas.py +++ b/src/backend/app/drones/drone_schemas.py @@ -1,8 +1,10 @@ +from typing import Optional from pydantic import BaseModel from fastapi import HTTPException from app.models.enums import HTTPStatus from psycopg import Connection from psycopg.rows import class_row +from datetime import datetime class BaseDrone(BaseModel): @@ -124,3 +126,44 @@ async def create(db: Connection, drone: DroneIn): status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=msg ) return new_drone_id[0] + + +class DroneFlightHeight(BaseModel): + id: int + country: str + country_code: str + max_altitude_ft: float + max_altitude_m: float + created_at: datetime + updated_at: Optional[datetime] = None + + @staticmethod + async def all(db: Connection): + try: + async with db.cursor(row_factory=class_row(DroneFlightHeight)) as cur: + await cur.execute(""" + SELECT * + FROM drone_flight_height; + """) + return await cur.fetchall() + + except Exception as e: + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e) + ) + + @staticmethod + async def one(db: Connection, country: str): + try: + async with db.cursor(row_factory=class_row(DroneFlightHeight)) as cur: + await cur.execute( + """ + SELECT * FROM drone_flight_height WHERE country = %(country)s; + """, + {"country": country}, + ) + return await cur.fetchone() + except Exception as e: + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e) + ) diff --git a/src/backend/app/migrations/versions/8ae4e43a7011_.py b/src/backend/app/migrations/versions/8ae4e43a7011_.py new file mode 100644 index 00000000..59a50843 --- /dev/null +++ b/src/backend/app/migrations/versions/8ae4e43a7011_.py @@ -0,0 +1,42 @@ +""" + +Revision ID: 8ae4e43a7011 +Revises: 2803924c6ff9 +Create Date: 2024-11-29 05:55:32.181491 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "8ae4e43a7011" +down_revision: Union[str, None] = "2803924c6ff9" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "drone_flight_height", + sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), + sa.Column("country", sa.String(), nullable=False), + sa.Column("country_code", sa.String(length=3), nullable=False), + sa.Column("max_altitude_ft", sa.Float(), nullable=False), + sa.Column("max_altitude_m", sa.Float(), nullable=False), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("updated_at", sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("country"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("drone_flight_height") + # ### end Alembic commands ###