Files
api/src/graphql_schema/entities/photo.py
T

158 lines
5.8 KiB
Python

import asyncio
from typing import List, Optional
import strawberry
from sqlalchemy import update
from strawberry.file_uploads import Upload
from background_jobs.elevation import add_terrain_elevation_to_photo
from database import models
from decorators.endpoints import authenticated_user_only
from dependencies.db import get_session
from graphql_schema.entities.types.types import Photo
from paths import get_photo_basepath
from upload_utils import (
handle_file_upload, delete_file, parse_exif_info, generate_thumbnail, resize_image,
rotate_image
)
from graphql_schema.entities.helpers.combobox import handle_combobox_save
from .resolvers.base import get_base_resolver, get_list
from graphql_schema.entities.types.mutation_input import ComboboxInput
@strawberry.type
class PhotoQueries:
@strawberry.field()
async def photos(root, info) -> List[Photo]:
query = get_base_resolver(models.Photo, user_id=info.context.user_id)
return await get_list(models.Photo, query)
@strawberry.type
class UploadPhotoMutation:
@strawberry.input
class UploadPhotoInput:
photo: Upload
flight_id: int
name: Optional[str] = None
description: Optional[str] = None
point_of_interest: Optional[ComboboxInput] = None
@strawberry.mutation
@authenticated_user_only()
async def upload_photo(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)
async with get_session() as db:
photo_model = await models.Photo.create(data={
"flight_id": input.flight_id,
"name": input.name,
"filename": filename,
"description": input.description,
"exposed_at": exif_info.get("datetime"),
"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,
"created_by_id": info.context.user_id,
}, db_session=db)
photo = Photo(**photo_model.as_dict())
# TODO: udelat primo konkretni bg joby na resize a thumbnaily
info.context.background_tasks.add_task(resize_image, path=path, filename=filename, new_width=2500, quality=85)
info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=filename)
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)
return photo
@strawberry.type
class EditPhotoMutation:
@strawberry.input
class EditPhotoInput:
name: Optional[str] = None
description: Optional[str] = None
point_of_interest: Optional[ComboboxInput] = None
is_flight_cover: Optional[bool] = None
@strawberry.mutation()
@authenticated_user_only()
async def edit_photo(self, info, id: int, input: EditPhotoInput) -> Photo:
# TODO: base trida pro inputy s definici to_dict/as_dict?
data = {
key: getattr(input, key) for key in ('name', 'description', 'is_flight_cover')
if getattr(input, key) is not None
}
query = get_base_resolver(models.Photo, user_id=info.context.user_id, object_id=id)
async with get_session() as db:
photo = (await db.scalars(query)).one()
if input.point_of_interest:
data['point_of_interest_id'] = await handle_combobox_save(
db,
models.PointOfInterest,
input.point_of_interest,
info.context.user_id,
extra_data={
"description": ""
}
)
if input.is_flight_cover:
# reset other covers
(await db.execute(
update(models.Photo)
.filter(models.Photo.flight_id == photo.flight_id)
.filter(models.Photo.id != id).values(is_flight_cover=False))
)
updated_model = await models.Photo.update(db, obj=photo, data=data)
return Photo(**updated_model.as_dict())
@strawberry.mutation()
@authenticated_user_only()
async def rotate_photo(self, info, id: int, angle: int) -> Photo:
async with get_session() as db:
query = get_base_resolver(models.Photo, user_id=info.context.user_id, object_id=id)
photo = (await db.scalars(query)).one()
await asyncio.gather(
rotate_image(
path=get_photo_basepath(photo.flight_id),
filename=photo.filename,
angle=angle,
),
rotate_image(
path=get_photo_basepath(photo.flight_id) + "/thumbs",
filename=photo.filename,
angle=angle,
),
)
return Photo(**photo.as_dict())
@strawberry.type
class DeletePhotoMutation:
@strawberry.mutation()
@authenticated_user_only()
async def delete_photo(self, info, id: int) -> Photo:
query = get_base_resolver(models.Photo, user_id=info.context.user_id, object_id=id)
async with get_session() as db:
photo_model = (await db.scalars(query)).one()
await db.delete(photo_model)
photo = Photo(**photo_model.as_dict())
base_path = get_photo_basepath(photo.flight_id)
delete_file(f"{base_path}/{photo.filename}", silent=True)
delete_file(f"{base_path}/thumbs/{photo.filename}", silent=True)
return photo_model