Slugy, webp, hromada zmen

This commit is contained in:
Michal Kváček
2024-01-13 23:29:24 +01:00
parent 3b1fd0351a
commit dabed327dd
26 changed files with 517 additions and 180 deletions
+18 -5
View File
@@ -29,12 +29,25 @@ class AircraftQueries:
@strawberry.field()
@error_logging
@allow_public
async def aircraft(root, info, id: int, public: Optional[bool] = False) -> Aircraft:
async def aircraft(
root, info,
id: Optional[int] = None,
call_sign: Optional[str] = None,
public: Optional[bool] = False
) -> Aircraft:
filter_params = {}
if id:
filter_params['object_id'] = id
if call_sign:
filter_params['call_sign'] = call_sign
return await AircraftQueryResolver().get_one(
id,
user_id=info.context.user_id,
organization_ids=info.context.organization_ids,
public=public
only_public=public,
**filter_params
)
@@ -44,13 +57,13 @@ class AircraftMutation:
@error_logging
@authenticated_user_only()
async def create_aircraft(root, info, input: CreateAircraftInput) -> Aircraft:
return await AircraftMutationResolver().create_new(input, info.context.user_id)
return await AircraftMutationResolver().create(info.context, input)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_aircraft(root, info, id: int, input: EditAircraftInput) -> Aircraft:
return await AircraftMutationResolver().edit(id, user_id=info.context.user_id, data=input)
return await AircraftMutationResolver().update(id, user_id=info.context.user_id, data=input)
@strawberry.mutation
@authenticated_user_only()
+4 -1
View File
@@ -19,4 +19,7 @@ class AirportQueries:
@error_logging
@authenticated_user_only()
async def airport(root, info, id: int) -> Airport:
return await BaseQueryResolver(Airport, models.Airport).get_one(id, user_id=info.context.user_id)
return await BaseQueryResolver(Airport, models.Airport).get_one(
object_id=id,
user_id=info.context.user_id
)
+16 -7
View File
@@ -4,6 +4,7 @@ from strawberry.types import Info
from database import models
from decorators.error_logging import error_logging
from decorators.endpoints import authenticated_user_only, allow_public
from graphql_schema.entities.helpers.detail import get_detail_filters
from graphql_schema.entities.resolvers.base import BaseMutationResolver
from graphql_schema.entities.resolvers.copilot import CopilotQueryResolver
from graphql_schema.entities.types.mutation_input import CreateCopilotInput, EditCopilotInput
@@ -21,11 +22,22 @@ class CopilotQueries:
@strawberry.field()
@error_logging
@allow_public
async def copilot(root, info: Info, id: int, pilot_username: Optional[str] = None) -> Copilot:
async def copilot(
root, info: Info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
pilot_username: Optional[str] = None,
public: Optional[bool] = False
) -> Copilot:
filter_params = get_detail_filters(id, url_slug)
if pilot_username:
filter_params['pilot_username'] = pilot_username
return await CopilotQueryResolver().get_one(
id,
user_id=info.context.user_id,
pilot_username=pilot_username
only_public=public,
**filter_params
)
@@ -35,10 +47,7 @@ class CopilotMutation:
@error_logging
@authenticated_user_only()
async def create_copilot(root, info, input: CreateCopilotInput) -> Copilot:
return await BaseMutationResolver(Copilot, models.Copilot).create(
data=input.to_dict(),
user_id=info.context.user_id
)
return await BaseMutationResolver(Copilot, models.Copilot).create(info.context, data=input)
@strawberry.mutation
@error_logging
+15 -5
View File
@@ -3,6 +3,7 @@ import strawberry
from database import models
from decorators.endpoints import authenticated_user_only, allow_public
from decorators.error_logging import error_logging
from graphql_schema.entities.helpers.detail import get_detail_filters
from graphql_schema.entities.helpers.pagination import PaginationWindow, get_pagination_window
from graphql_schema.entities.resolvers.base import BaseMutationResolver
from graphql_schema.entities.resolvers.event import EventQueryResolver
@@ -40,12 +41,21 @@ class EventQueries:
@strawberry.field()
@error_logging
@allow_public
async def event(root, info, id: int, username: Optional[str] = None, public: Optional[bool] = False) -> Event:
async def event(
root, info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
username: Optional[str] = None,
public: Optional[bool] = False
) -> Event:
filter_params = get_detail_filters(id, url_slug)
if username:
filter_params['username'] = username
return await EventQueryResolver().get_one(
id,
username=username,
public=public,
user_id=info.context.user_id
user_id=info.context.user_id ,
** filter_params
)
@@ -55,7 +65,7 @@ class EventMutation:
@error_logging
@authenticated_user_only()
async def create_event(root, info, input: CreateEventInput) -> Event:
return await BaseMutationResolver(Event, models.Event).create(input.to_dict(), info.context.user_id)
return await BaseMutationResolver(Event, models.Event).create(info.context, input)
@strawberry.mutation
@error_logging
+15 -41
View File
@@ -10,6 +10,7 @@ from graphql_schema.entities.resolvers.flight import handle_aircraft_save, Fligh
from graphql_schema.entities.types.mutation_input import EditFlightInput, CreateFlightInput
from graphql_schema.entities.types.types import Flight
from .helpers.combobox import handle_combobox_save
from .helpers.detail import get_detail_filters
from .helpers.pagination import PaginationWindow, get_pagination_window
@@ -50,12 +51,21 @@ class FlightQueries:
@strawberry.field()
@error_logging
@allow_public
async def flight(root, info, id: int, username: Optional[str] = None, public: Optional[bool] = False) -> Flight:
async def flight(
root, info,
id: Optional[int] = None,
url_slug: Optional[str] = None,
username: Optional[str] = None,
public: Optional[bool] = False
) -> Flight:
filter_params = get_detail_filters(id, url_slug)
if username:
filter_params['username'] = username
return await FlightQueryResolver().get_one(
id,
user_id=info.context.user_id,
username=username,
public=public
only_public=public,
**filter_params
)
@@ -65,43 +75,7 @@ class FlightMutation:
@error_logging
@authenticated_user_only()
async def create_flight(self, info, input: CreateFlightInput) -> Flight:
data = input.to_dict()
user_id = info.context.user_id
async with get_session() as db:
aircraft_id = await handle_aircraft_save(db, info.context.user_id, input.aircraft)
# TODO: tohle je blbost, bude to vyrabet dve stejne instance!
takeoff_airport_id, landing_airport_id = await asyncio.gather(
handle_combobox_save(
db, models.Airport, input.takeoff_airport, user_id, name_column="icao_code",
extra_data={"name": input.takeoff_airport.name}
),
handle_combobox_save(
db, models.Airport, input.landing_airport, user_id, name_column="icao_code",
extra_data={"name": input.landing_airport.name}
)
)
data.update({
"takeoff_airport_id": takeoff_airport_id,
"landing_airport_id": landing_airport_id,
"aircraft_id": aircraft_id,
"has_terrain_elevation": False,
"name": "",
"description": ""
})
flight = await FlightMutationResolver().create(data, info.context.user_id)
info.context.background_tasks.add_task(
download_weather,
flight_id=flight.id, airport_id=takeoff_airport_id, date_time=flight.takeoff_datetime, type_="takeoff"
)
info.context.background_tasks.add_task(
download_weather,
flight_id=flight.id, airport_id=landing_airport_id, date_time=flight.landing_datetime, type_="landing"
)
return flight
return await FlightMutationResolver().create(info.context, input)
@strawberry.mutation
@error_logging
@@ -15,10 +15,11 @@ async def handle_combobox_save(
if input.id:
return input.id
else:
if not extra_data:
extra_data = {}
# TODO: vyhledat podle `name_column` existujici zaznam, pokud nebude, az pak vytvorit
data = {name_column: input.name, **extra_data}
if hasattr(model, "created_by_id"):
data["created_by_id"] = user_id
@@ -0,0 +1,15 @@
from typing import Optional
from graphql import GraphQLError
def get_detail_filters(id: Optional[int], url_slug: Optional[str]) -> dict:
filter_params = {}
if id:
filter_params['object_id'] = id
if url_slug is not None:
filter_params['url_slug'] = url_slug
if not filter_params:
raise GraphQLError("You must specifiy either urlSlug or id!")
return filter_params
+12 -15
View File
@@ -19,14 +19,18 @@ class OrganizationQueries:
@authenticated_user_only()
async def organizations(root, info) -> List[Organization]:
return await BaseQueryResolver(Organization, models.Organization).get_list(
info.context.user_id, order_by=[models.Organization.name]
info.context.user_id,
order_by=[models.Organization.name]
)
@strawberry.field()
@error_logging
@authenticated_user_only()
async def organization(root, info, id: int) -> Organization:
return await BaseQueryResolver(Organization, models.Organization).get_one(id, info.context.user_id)
return await BaseQueryResolver(Organization, models.Organization).get_one(
object_id=id,
user_id=info.context.user_id
)
@strawberry.type
@@ -36,24 +40,17 @@ class OrganizationMutation:
@error_logging
@authenticated_user_only()
async def create_organization(root, info, input: CreateOrganizationInput) -> Organization:
return await BaseMutationResolver(Organization, models.Organization).create(
data=input.to_dict(),
user_id=info.context.user_id
)
return await BaseMutationResolver(Organization, models.Organization).create(info.context, data=input)
@strawberry.mutation
@error_logging
@authenticated_user_only()
async def edit_organization(root, info, id: int, input: EditOrganizationInput) -> Organization:
async with get_session() as db:
organization = (await db.scalars(
BaseQueryResolver(Organization, models.Organization).get_query(
user_id=info.context.user_id, object_id=id
)
)).one()
updated_organization = await models.Organization.update(db, obj=organization, data=input.to_dict())
return Organization(**updated_organization.as_dict())
return await BaseMutationResolver(Organization, models.Organization).update(
id,
data=input,
user_id=info.context.user_id
)
@strawberry.type
+5 -1
View File
@@ -36,7 +36,11 @@ class PhotoQueries:
@error_logging
@allow_public
async def photo(root, info, id: int, public: Optional[bool] = False,) -> Photo:
return await BaseQueryResolver(Photo, models.Photo).get_one(id, user_id=info.context.user_id, public=public)
return await BaseQueryResolver(Photo, models.Photo).get_one(
object_id=id,
user_id=info.context.user_id,
only_public=public
)
@strawberry.type
+19 -8
View File
@@ -1,16 +1,18 @@
from typing import List
from typing import List, Optional
import strawberry
from graphql import GraphQLError
from database import models
from decorators.endpoints import authenticated_user_only, allow_public
from database.transaction import get_session
from decorators.error_logging import error_logging
from graphql_schema.entities.helpers.combobox import handle_combobox_save
from graphql_schema.entities.helpers.detail import get_detail_filters
from graphql_schema.entities.helpers.pagination import get_pagination_window, PaginationWindow
from graphql_schema.entities.resolvers.base import BaseQueryResolver, BaseMutationResolver
from graphql_schema.entities.types.types import PointOfInterest
from graphql_schema.entities.types.mutation_input import CreatePointOfInterestInput, EditPointOfInterestInput
@strawberry.type
class PointOfInterestQueries:
@strawberry.field()
@@ -33,10 +35,18 @@ class PointOfInterestQueries:
@strawberry.field()
@allow_public
async def point_of_interest(root, info, id: int, public: bool = False) -> PointOfInterest:
async def point_of_interest(
root, info,
url_slug: Optional[str] = None,
id: Optional[int] = None,
public: bool = False
) -> PointOfInterest:
filter_params = get_detail_filters(id, url_slug)
return await BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_one(
id, info.context.user_id,
only_public=public
user_id=info.context.user_id,
only_public=public,
**filter_params
)
@@ -54,9 +64,10 @@ class PointOfInterestMutation:
db, models.PointOfInterestType, input.type, info.context.user_id
)
return await BaseMutationResolver(PointOfInterest, models.PointOfInterest).create(
input_data, info.context.user_id
)
input_data['created_by_id'] = info.context.user_id
return await BaseMutationResolver(PointOfInterest, models.PointOfInterest)._do_create(
db, input_data
)
@strawberry.mutation
@error_logging
+2 -1
View File
@@ -21,7 +21,8 @@ class PointOfInterestTypeQueries:
@authenticated_user_only()
async def point_of_interest_type(root, info, id: int) -> PointOfInterestType:
return await BaseQueryResolver(PointOfInterestType, models.PointOfInterestType).get_one(
id, info.context.user_id
object_id=id,
user_id=info.context.user_id
)
#
@@ -21,10 +21,17 @@ class AircraftQueryResolver(BaseQueryResolver):
*args,
**kwargs,
):
call_sign = kwargs.get("call_sign")
filters = {}
if object_id:
filters['object_id'] = object_id
if call_sign:
filters['call_sign'] = call_sign
query = super().get_query(
order_by=[models.Aircraft.id.desc()],
object_id=object_id,
user_id=user_id if not organization_ids else None
user_id=user_id if not organization_ids else None,
**filters
)
if organization_ids:
query = (
@@ -43,20 +50,22 @@ class AircraftMutationResolver(BaseMutationResolver):
def __init__(self):
super().__init__(graphql_type=Aircraft, model=models.Aircraft)
async def create_new(self, data: CreateAircraftInput, user_id: int) -> Aircraft:
async def create(self, context, data: CreateAircraftInput) -> Aircraft:
input_data = data.to_dict()
if data.organization:
async with get_session() as db:
async with get_session() as db:
input_data['created_by_id'] = context.user_id
if data.organization:
input_data['organization_id'] = await handle_combobox_save(
db,
models.Organization,
input=data.organization,
user_id=user_id,
user_id=context.user_id,
)
return await self.create(data=input_data, user_id=user_id)
return await self._do_create(db, data=input_data)
async def edit(self, id: int, user_id: int, data: EditAircraftInput) -> Aircraft:
async def update(self, id: int, user_id: int, data: EditAircraftInput) -> Aircraft:
update_data = data.to_dict()
async with get_session() as db:
if data.organization:
@@ -66,5 +75,5 @@ class AircraftMutationResolver(BaseMutationResolver):
input=data.organization,
user_id=user_id,
)
aircraft = await models.Aircraft.update(db, id=id, data=update_data)
return Aircraft(**aircraft.as_dict())
return await self._do_update(db, id, update_data)
+18 -15
View File
@@ -33,7 +33,6 @@ class BaseQueryResolver(BaseResolver):
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
*args,
**kwargs,
):
query = self.query_builder.get_simple_query(created_by_id=user_id, order_by=order_by, only_public=only_public)
@@ -52,8 +51,8 @@ class BaseQueryResolver(BaseResolver):
query = self.get_query(user_id, **kwargs)
return await self._get_list(query)
async def get_one(self, id: int, user_id: Optional[int] = None, **kwargs) -> GQL_TYPE:
query = self.get_query(user_id, object_id=id, **kwargs)
async def get_one(self, user_id: Optional[int] = None, **kwargs) -> GQL_TYPE:
query = self.get_query(user_id, **kwargs)
return await self._get_one(query)
@@ -62,21 +61,31 @@ class BaseMutationResolver(BaseResolver):
query = self.query_builder.get_simple_query(created_by_id=created_by_id).filter(self.model.id == id)
return (await db.scalars(query)).one()
async def _do_create(self, data) -> GQL_TYPE:
async with get_session() as db:
model = await self.model.create(db, data=data)
return self.graphql_type(**model.as_dict())
async def _do_create(self, db: AsyncSession, data: dict) -> GQL_TYPE:
model = await self.model.create(db, data=data)
return self.graphql_type(**model.as_dict())
async def _do_update(self, db: AsyncSession, obj: models.BaseModel | dict, data: dict) -> GQL_TYPE:
async def _do_update(self, db: AsyncSession, obj: models.BaseModel | dict | int, data: dict) -> GQL_TYPE:
update_where = {}
if isinstance(obj, models.BaseModel):
update_where['obj'] = obj
else:
elif isinstance(obj, dict):
update_where['id'] = obj['id']
else:
update_where['id'] = obj
model = await self.model.update(db, data=data, **update_where)
return self.graphql_type(**model.as_dict())
async def create(self, context, data: BaseGraphqlInputType) -> GQL_TYPE:
input_data = data.to_dict()
if hasattr(self.model, "created_by_id"):
input_data['created_by_id'] = context.user_id
async with get_session() as db:
return await self._do_create(db, input_data)
async def update(self, id: int, data: BaseGraphqlInputType, user_id: int) -> GQL_TYPE:
async with get_session() as db:
item = await self._get_one(db, id, user_id)
@@ -93,9 +102,3 @@ class BaseMutationResolver(BaseResolver):
return self.graphql_type(**model.as_dict())
async def create(self, data: dict, user_id: Optional[int] = None) -> GQL_TYPE:
input_data = {**data}
if hasattr(self.model, "created_by_id"):
input_data['created_by_id'] = user_id
return await self._do_create(input_data)
+107 -10
View File
@@ -1,6 +1,6 @@
import asyncio
from typing import List, Optional
from sqlalchemy import delete, insert
from sqlalchemy import delete, insert, select, func, text
from sqlalchemy.ext.asyncio import AsyncSession
from strawberry.file_uploads import Upload
from background_jobs.elevation import add_terrain_elevation_to_flight
@@ -8,9 +8,10 @@ from background_jobs.weather import download_weather
from database import models
from database.models import flight_has_copilot
from database.transaction import get_session
from external.gpx_parser import GPXParser
from graphql_schema.entities.helpers.combobox import handle_combobox_save
from graphql_schema.entities.resolvers.base import BaseMutationResolver, BaseQueryResolver
from graphql_schema.entities.types.mutation_input import EditFlightInput, TrackItemInput, ComboboxInput
from graphql_schema.entities.resolvers.base import BaseMutationResolver, BaseQueryResolver, GQL_TYPE
from graphql_schema.entities.types.mutation_input import EditFlightInput, TrackItemInput, ComboboxInput, CreateFlightInput
from graphql_schema.entities.types.types import Flight
from paths import FLIGHT_GPX_TRACK_PATH
from utils.file import delete_file
@@ -25,12 +26,20 @@ class FlightQueryResolver(BaseQueryResolver):
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
url_slug: Optional[str] = None,
only_public: Optional[bool] = False,
*args,
**kwargs
):
filters = {}
if object_id:
filters['object_id'] = object_id
if url_slug:
filters['url_slug'] = url_slug
query = super().get_query(
user_id, object_id,
user_id,
**filters,
order_by=[models.Flight.takeoff_datetime.desc()],
only_public=only_public
)
@@ -66,6 +75,90 @@ class FlightMutationResolver(BaseMutationResolver):
def __init__(self):
super().__init__(Flight, models.Flight)
async def get_airport_id_by_gps(self, gps_lat: float, gps_lng: float) -> Optional[int]:
async with get_session() as db:
query = select(
models.Airport,
func.coalesce((6371 * func.acos(
func.cos(func.radians(gps_lat)) *
func.cos(func.radians(models.Airport.gps_latitude)) *
func.cos(func.radians(models.Airport.gps_longitude) - func.radians(gps_lng)) +
func.sin(func.radians(gps_lat)) *
func.sin(func.radians(models.Airport.gps_latitude))
)), 9999).label("distance")
).filter(models.Airport.use_in_gpx_guess.is_(True)).order_by("distance").having(text("distance < 1")).limit(1)
data = (await db.execute(query)).one_or_none()
if data:
airport, distance = data
return airport.id
return None
async def extract_data_from_gpx(self, gpx_filename: str) -> dict:
data = GPXParser(f"{FLIGHT_GPX_TRACK_PATH}/{gpx_filename}")
times, coordinates = await asyncio.gather(
data.get_times(),
data.get_coordinates()
)
takeoff_airport_id, landing_airport_id = await asyncio.gather(
self.get_airport_id_by_gps(coordinates[0]['lat'], coordinates[0]['lng']),
self.get_airport_id_by_gps(coordinates[-1]['lat'], coordinates[-1]['lng']),
)
return {
"takeoff_airport_id": takeoff_airport_id,
"landing_airport_id": landing_airport_id,
"takeoff_datetime": times[0],
"landing_datetime": times[-1],
}
async def create(self, context, input: CreateFlightInput) -> Flight:
data = input.to_dict()
user_id = context.user_id
if input.gpx_track_file:
data['gpx_track_filename'] = await handle_upload_gpx(gpx_track=input.gpx_track_file, context=context)
data_from_gpx = await self.extract_data_from_gpx(data['gpx_track_filename'])
data.update(data_from_gpx)
else:
async with get_session() as db:
data.update({
"takeoff_airport_id": await handle_combobox_save(
db, models.Airport, input.takeoff_airport, user_id, name_column="icao_code",
extra_data={"name": input.takeoff_airport.name}
),
"landing_airport_id": await handle_combobox_save(
db, models.Airport, input.landing_airport, user_id, name_column="icao_code",
extra_data={"name": input.landing_airport.name}
),
})
async with get_session() as db:
aircraft_id = await handle_aircraft_save(db, context.user_id, input.aircraft)
data.update({
"aircraft_id": aircraft_id,
"has_terrain_elevation": False,
"name": "",
"description": "",
"created_by_id": context.user_id
})
flight = await self._do_create(db, data)
context.background_tasks.add_task(
download_weather,
flight_id=flight.id, airport_id=flight.takeoff_airport_id, date_time=flight.takeoff_datetime, type_="takeoff"
)
context.background_tasks.add_task(
download_weather,
flight_id=flight.id, airport_id=flight.landing_airport_id, date_time=flight.landing_datetime, type_="landing"
)
return flight
async def update(self, context, id: int, input: EditFlightInput) -> Flight:
user_id = context.user_id
async with get_session() as db:
@@ -75,10 +168,11 @@ class FlightMutationResolver(BaseMutationResolver):
data = input.to_dict()
if input.gpx_track is not None:
data['gpx_track_filename'] = await handle_upload_gpx(flight_data['gpx_track_filename'], input.gpx_track)
context.background_tasks.add_task(
add_terrain_elevation_to_flight, flight_id=id, gpx_filename=data['gpx_track_filename']
if input.gpx_track_file is not None:
data['gpx_track_filename'] = await handle_upload_gpx(
gpx_track=input.gpx_track_file,
context=context,
original_gpx_filename=flight_data['gpx_track_filename']
)
async with get_session() as db:
@@ -136,11 +230,14 @@ class FlightMutationResolver(BaseMutationResolver):
return await self._do_update(db, flight_data, data)
async def handle_upload_gpx(original_gpx_filename: str, gpx_track: Upload):
async def handle_upload_gpx(gpx_track: Upload, context, original_gpx_filename: Optional[str] = None):
if original_gpx_filename:
delete_file(FLIGHT_GPX_TRACK_PATH + "/" + original_gpx_filename, silent=True)
return await handle_file_upload(gpx_track, FLIGHT_GPX_TRACK_PATH)
filename = await handle_file_upload(gpx_track, FLIGHT_GPX_TRACK_PATH)
context.background_tasks.add_task(add_terrain_elevation_to_flight, flight_id=id, gpx_filename=filename)
return filename
async def handle_track_edit(db: AsyncSession, flight_id: int, track: List[TrackItemInput], user_id: int):
+44 -26
View File
@@ -1,5 +1,6 @@
import os
import shutil
from time import time
from typing import Optional
from PIL import Image
from pydantic import BaseModel
@@ -91,29 +92,35 @@ class PhotoMutationResolver(BaseMutationResolver):
async def upload(self, info, input: UploadPhotoInput) -> Photo:
path = get_photo_basepath(input.flight_id)
filename = await handle_file_upload(input.photo, path)
exif_info = await parse_exif_info(path, filename)
img_name = await handle_file_upload(input.photo, path, uid_prefix=False, overwrite=False)
img = Image.open(f"{path}/{filename}")
photo = await PhotoMutationResolver().create(
user_id=info.context.user_id,
data={
"flight_id": input.flight_id,
"name": input.name,
"filename": filename,
"width": img.width,
"height": img.height,
"description": input.description,
"exposed_at": exif_info.get("datetime_original"),
"gps_latitude": exif_info.get("gps_latitude"),
"gps_longitude": exif_info.get("gps_longitude"),
"gps_altitude": exif_info.get("gps_altitude"),
"is_flight_cover": False,
},
)
exif_info = await parse_exif_info(path, img_name)
info.context.background_tasks.add_task(resize_photo, path=path, filename=filename, photo_id=photo.id)
info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=filename)
img = Image.open(f"{path}/{img_name}")
filename, filename_ext = os.path.splitext(img_name)
async with get_session() as db:
photo = await PhotoMutationResolver()._do_create(
db,
data={
"flight_id": input.flight_id,
"name": input.name,
"filename": filename,
"filename_extension": filename_ext[1:], # nechci ukladat tecku na zacatku
"cache_key": int(time()),
"width": img.width,
"height": img.height,
"description": input.description,
"exposed_at": exif_info.get("datetime_original"),
"gps_latitude": exif_info.get("gps_latitude"),
"gps_longitude": exif_info.get("gps_longitude"),
"gps_altitude": exif_info.get("gps_altitude"),
"created_by_id": info.context.user_id,
},
)
info.context.background_tasks.add_task(resize_photo, path=path, filename=img_name, photo_id=photo.id)
info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=img_name)
if exif_info.get("gps_latitude") and exif_info.get("gps_longitude"):
info.context.background_tasks.add_task(add_terrain_elevation_to_photo, photo=photo)
@@ -164,7 +171,8 @@ class PhotoMutationResolver(BaseMutationResolver):
async with get_session() as db:
return await self._do_update(db, obj={"id": id}, data={
"width": editor.img.width,
"height": editor.img.height
"height": editor.img.height,
"cache_key": int(time())
})
async def adjust(self, id: int, user_id: int, adjustment: AdjustmentInput, info):
@@ -209,15 +217,25 @@ class PhotoMutationResolver(BaseMutationResolver):
return await self._do_update(db, obj={"id": id}, data={
"width": editor.img.width,
"height": editor.img.height
"height": editor.img.height,
"cache_key": int(time())
})
async def delete(self, user_id: int, id: int) -> Photo:
photo = await super().delete(user_id, id)
base_path = get_photo_basepath(photo.flight_id)
delete_file(f"{base_path}/{photo.filename}", silent=True)
delete_file(f"{base_path}/_original_{photo.filename}", silent=True)
delete_file(f"{base_path}/thumbs/{photo.filename}", silent=True)
files_to_delete = [
photo.filename,
f"_original_{photo.filename}",
f"{photo.filename}.{photo.filename_extension}",
f"_original_{photo.filename}.{photo.filename_extension}",
f"thumbs/{photo.filename}",
f"thumbs/{photo.filename}.{photo.filename_extension}",
f"thumbs/{photo.filename}.webp",
]
for filename in files_to_delete:
delete_file(f"{base_path}/{filename}", silent=True)
return photo
@@ -102,7 +102,7 @@ class AdjustmentInput:
"takeoff_weather_info_id", "landing_weather_info_id", "gpx_track_filename", "event_id"
], all_optional=True)
class EditFlightInput(BaseGraphqlInputType):
gpx_track: Optional[Upload] = None # TODO: poresit validaci uploadovaneho souboru!
gpx_track_file: Optional[Upload] = None # TODO: poresit validaci uploadovaneho souboru!
track: Optional[List[TrackItemInput]] = None
copilots: Optional[List[ComboboxInput]] = None
aircraft: Optional[ComboboxInput] = None
@@ -114,10 +114,11 @@ class EditFlightInput(BaseGraphqlInputType):
@strawberry.input()
class CreateFlightInput(BaseGraphqlInputType):
aircraft: ComboboxInput
landing_airport: ComboboxInput
takeoff_airport: ComboboxInput
takeoff_datetime: datetime
landing_datetime: datetime
gpx_track_file: Optional[Upload] = None # TODO: poresit validaci uploadovaneho souboru!
landing_airport: Optional[ComboboxInput] = None
takeoff_airport: Optional[ComboboxInput] = None
takeoff_datetime: Optional[datetime] = None
landing_datetime: Optional[datetime] = None
@strawberry.input()
+6 -2
View File
@@ -128,8 +128,12 @@ class Flight:
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))
takeoff_airport: Airport = strawberry.field(resolver=lambda root: airport_dataloader.load(root.takeoff_airport_id))
landing_airport: Airport = strawberry.field(resolver=lambda root: airport_dataloader.load(root.landing_airport_id))
takeoff_airport: Optional[Airport] = strawberry.field(
resolver=lambda root: airport_dataloader.load(root.takeoff_airport_id)
)
landing_airport: Optional[Airport] = strawberry.field(
resolver=lambda root: airport_dataloader.load(root.landing_airport_id)
)
title_photo: Optional[Photo] = strawberry.field(resolver=lambda root: photo_dataloader.load(root.title_photo_id))
track: List[FlightTrack] = strawberry.field(resolver=lambda root: flight_track_dataloader.load(root.id))
takeoff_weather_info: Optional[WeatherInfo] = strawberry.field(