diff --git a/src/graphql_schema/dataloaders/flight_duration.py b/src/graphql_schema/dataloaders/flight_duration.py new file mode 100644 index 0000000..96fc02b --- /dev/null +++ b/src/graphql_schema/dataloaders/flight_duration.py @@ -0,0 +1,24 @@ +from typing import List +from sqlalchemy import select, func +from strawberry.dataloader import DataLoader +from database import async_session, models + + +async def load_flight_durations(ids: List[int]): + async with async_session() as db: + flights = (await db.execute( + select( + models.Flight.id, + func.timediff(models.Flight.landing_datetime, models.Flight.takeoff_datetime).label("diff"), + func.coalesce(func.sum(models.FlightTrack.landing_duration), 0).label("landing_duration") + ).join(models.Flight.track, isouter=True) + .group_by(models.Flight.id) + .filter(models.Flight.id.in_(ids)) + + )).all() + + items_by_id = {item.id: item.diff.seconds // 60 - item.landing_duration for item in flights} + return [items_by_id.get(id_) for id_ in ids] + + +flight_duration_dataloader = DataLoader(load_fn=load_flight_durations, cache=False) diff --git a/src/graphql_schema/entities/photo.py b/src/graphql_schema/entities/photo.py index e99ef8b..904a052 100644 --- a/src/graphql_schema/entities/photo.py +++ b/src/graphql_schema/entities/photo.py @@ -18,8 +18,6 @@ from .resolvers.base import get_base_resolver, get_list from graphql_schema.entities.types.mutation_input import ComboboxInput - - @strawberry.type class PhotoQueries: @strawberry.field() diff --git a/src/graphql_schema/entities/resolvers/flight.py b/src/graphql_schema/entities/resolvers/flight.py index e303448..a9ff5cf 100644 --- a/src/graphql_schema/entities/resolvers/flight.py +++ b/src/graphql_schema/entities/resolvers/flight.py @@ -1,12 +1,10 @@ import asyncio from datetime import datetime from typing import List, Optional, Type - from aiocache import cached from sqlalchemy import delete, insert from sqlalchemy.ext.asyncio import AsyncSession from strawberry.file_uploads import Upload - from background_jobs.elevation import add_terrain_elevation_to_flight from database import models from database.models import flight_has_copilot @@ -93,7 +91,9 @@ class FlightMutationResolver(BaseMutationResolver): data['landing_airport_id'] = landing_airport.id data['landing_datetime'] = landing_datetime - weather_info = await handle_weather_info(db, landing_datetime, landing_airport, flight_data['landing_weather_info_id']) + weather_info = await handle_weather_info( + db, landing_datetime, landing_airport, flight_data['landing_weather_info_id'] + ) if weather_info: data['landing_weather_info_id'] = weather_info.id @@ -104,7 +104,9 @@ class FlightMutationResolver(BaseMutationResolver): data['takeoff_airport_id'] = takeoff_airport.id data['takeoff_datetime'] = takeoff_datetime - weather_info = await handle_weather_info(db, takeoff_datetime, takeoff_airport, flight_data['takeoff_weather_info_id']) + weather_info = await handle_weather_info( + db, takeoff_datetime, takeoff_airport, flight_data['takeoff_weather_info_id'] + ) if weather_info: data['takeoff_weather_info_id'] = weather_info.id diff --git a/src/graphql_schema/entities/types/types.py b/src/graphql_schema/entities/types/types.py index d665d41..58526bb 100644 --- a/src/graphql_schema/entities/types/types.py +++ b/src/graphql_schema/entities/types/types.py @@ -1,15 +1,22 @@ from __future__ import annotations -from datetime import datetime, timedelta +from datetime import datetime from typing import Optional, Annotated, List import strawberry -from sqlalchemy import func, select from config import API_URL from database import models from decorators.endpoints import authenticated_user_only -from dependencies.db import get_session from external.gpx_parser import GPXParser -from graphql_schema.dataloaders.multi_models import poi_photos_dataloader, flight_by_poi_dataloader, flight_copilots_dataloader, flight_track_dataloader, photos_dataloader, flights_by_aircraft_dataloader, users_in_organization_dataloader, aircrafts_from_organization_dataloader, user_organizations_dataloader, flights_by_event_dataloader, flights_by_copilot_dataloader -from graphql_schema.dataloaders.single_model import poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader, cover_photo_loader, airport_weather_info_loader, organizations_dataloader +from graphql_schema.dataloaders.flight_duration import flight_duration_dataloader +from graphql_schema.dataloaders.multi_models import ( + poi_photos_dataloader, flight_by_poi_dataloader, flight_copilots_dataloader, flight_track_dataloader, + photos_dataloader, flights_by_aircraft_dataloader, users_in_organization_dataloader, + aircrafts_from_organization_dataloader, user_organizations_dataloader, flights_by_event_dataloader, + flights_by_copilot_dataloader +) +from graphql_schema.dataloaders.single_model import ( + poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader, cover_photo_loader, + airport_weather_info_loader, organizations_dataloader +) from graphql_schema.entities.airport import Airport from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type from paths import get_photo_basepath @@ -87,21 +94,6 @@ class GPXTrack: @strawberry_sqlalchemy_type(models.Flight) class Flight: - async def duration_min_calculated(root): - # TODO: predelat na dataloader, dobu nacitat v DB - diff: timedelta = root.landing_datetime - root.takeoff_datetime - total_time_minutes = diff.seconds / 60 - - async with get_session() as db: - landing_durations = (await db.scalars( - select(func.sum(models.FlightTrack.landing_duration)) - .filter(models.FlightTrack.airport_id.isnot(None)) - .filter(models.FlightTrack.flight_id == root.id) - )).one() or 0 - - print(landing_durations) - return total_time_minutes - float(landing_durations) - async def load_gpx_track(root): if not root.gpx_track_filename: return None @@ -132,7 +124,6 @@ class Flight: async def load_event(root): return await event_dataloader.load(root.event_id) - duration_min_calculated: int = strawberry.field(resolver=duration_min_calculated) copilots: Optional[List[Copilot]] = strawberry.field(resolver=load_copilots) event: Optional[Event] = strawberry.field(resolver=load_event) aircraft: Aircraft = strawberry.field(resolver=lambda root: aircraft_dataloader.load(root.aircraft_id)) @@ -147,10 +138,10 @@ class Flight: resolver=lambda root: airport_weather_info_loader.load(root.landing_weather_info_id) ) photos: List[Photo] = strawberry.field(resolver=lambda root: photos_dataloader.load(root.id)) - gpx_track_url: Optional[str] = strawberry.field( - resolver=lambda root: get_public_url(f"/tracks/{root.gpx_track_filename}") if root.gpx_track_filename else None - ) # TODO: odstranit gpx_track: Optional[GPXTrack] = strawberry.field(resolver=load_gpx_track) + duration_min_calculated: int = strawberry.field( + resolver=lambda root: flight_duration_dataloader.load(root.id) + ) @strawberry_sqlalchemy_type(models.Copilot) diff --git a/src/paths.py b/src/paths.py index 46d984b..4dd974d 100644 --- a/src/paths.py +++ b/src/paths.py @@ -2,5 +2,6 @@ PHOTO_BASE_PATH = "" AIRCRAFT_BASE_PATH = "" FLIGHT_BASE_PATH = "" + def get_photo_basepath(flight_id: int) -> str: return f"/app/uploads/photos/{flight_id}"