From 5d85e0c0f61cb91dc77409cd357ef33a6d185a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Kv=C3=A1=C4=8Dek?= Date: Mon, 28 Aug 2023 22:02:02 +0200 Subject: [PATCH] Refaktoring, bugifxy --- src/database/config.py | 5 +- src/graphql_schema/dataloaders/photos.py | 1 - src/graphql_schema/entities/flight.py | 3 -- src/graphql_schema/entities/photo.py | 61 ++++++++++++++++++------ src/graphql_schema/entities/poi.py | 19 ++++---- src/graphql_schema/entities/user.py | 16 +++---- src/upload_utils.py | 23 ++++----- 7 files changed, 72 insertions(+), 56 deletions(-) diff --git a/src/database/config.py b/src/database/config.py index 0aad836..58ca878 100644 --- a/src/database/config.py +++ b/src/database/config.py @@ -1,6 +1,5 @@ import os -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession -from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker def get_database_url(): @@ -19,4 +18,4 @@ def create_db_engine(): engine = create_db_engine() -async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) +async_session = async_sessionmaker(engine, expire_on_commit=True, class_=AsyncSession) diff --git a/src/graphql_schema/dataloaders/photos.py b/src/graphql_schema/dataloaders/photos.py index 62c5752..43c9cb0 100644 --- a/src/graphql_schema/dataloaders/photos.py +++ b/src/graphql_schema/dataloaders/photos.py @@ -18,7 +18,6 @@ class PhotoDataloader: select(Photo) .filter(getattr(Photo, self.relationship_column).in_(ids)) )).all() - photos_by_relationship_id = defaultdict(list) for photo in models: photos_by_relationship_id[getattr(photo, self.relationship_column)].append(photo) diff --git a/src/graphql_schema/entities/flight.py b/src/graphql_schema/entities/flight.py index fe6435b..8925a21 100644 --- a/src/graphql_schema/entities/flight.py +++ b/src/graphql_schema/entities/flight.py @@ -173,9 +173,6 @@ class CreateFlightMutation: ) await db.flush() - print("XXXXXXXXXXXXX", weather_takeoff, weather_takeoff.id) - - flight = await models.Flight.create(db, data={ **data, "takeoff_weather_info_id": weather_takeoff.id, diff --git a/src/graphql_schema/entities/photo.py b/src/graphql_schema/entities/photo.py index 97033a1..3d66235 100644 --- a/src/graphql_schema/entities/photo.py +++ b/src/graphql_schema/entities/photo.py @@ -1,11 +1,18 @@ -from typing import List +from typing import List, Optional, Annotated, TYPE_CHECKING import strawberry from sqlalchemy import select, update from strawberry.file_uploads import Upload from database import models from decorators.endpoints import authenticated_user_only -from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type, strawberry_sqlalchemy_input +from graphql_schema.dataloaders.poi import poi_dataloader +from graphql_schema.entities.poi import PointOfInterest +from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type +from graphql_schema.types import ComboboxInput from upload_utils import get_public_url, handle_file_upload, delete_file, parse_exif_info, generate_thumbnail, file_exists, resize_image +from .helpers.flight import handle_combobox_save + +if TYPE_CHECKING: + from .poi import PointOfInterest @strawberry_sqlalchemy_type(models.Photo) @@ -14,14 +21,21 @@ class Photo: return get_public_url(f"photos/{root.flight_id}/{root.filename}") def resolve_thumb_url(root): - thumbnail = get_photo_basepath(root.flight_id)+"/thumbs/"+root.filename + thumbnail = get_photo_basepath(root.flight_id) + "/thumbs/" + root.filename if not file_exists(thumbnail): return get_public_url(f"photos/{root.flight_id}/{root.filename}") return get_public_url(f"photos/{root.flight_id}/thumbs/{root.filename}") + async def load_poi(root): + if not root.point_of_interest_id: + return None + + return await poi_dataloader.load(root.point_of_interest_id) + url: str = strawberry.field(resolver=resolve_url) thumbnail_url: str = strawberry.field(resolver=resolve_thumb_url) + point_of_interest: Optional[Annotated["PointOfInterest", strawberry.lazy('.poi')]] = strawberry.field(resolver=load_poi) def get_base_query(user_id: int): @@ -46,11 +60,13 @@ class PhotoQueries: @strawberry.type class UploadPhotoMutation: - @strawberry_sqlalchemy_input(models.Photo, exclude_fields=[ - "id", "filename", "is_flight_cover", "exposed_at", "gps_latitude", "gps_longitude", "gps_altitude" - ]) + @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() @@ -59,11 +75,11 @@ class UploadPhotoMutation: filename = await handle_file_upload(input.photo, path) info.context.background_tasks.add_task(resize_image, path=path, filename=filename, new_width=2500) - info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=filename, new_width=300) + info.context.background_tasks.add_task(generate_thumbnail, path=path, filename=filename) exif_info = await parse_exif_info(path, filename) - created_photo = await models.Photo.create(data={ + return await models.Photo.create(data={ "flight_id": input.flight_id, "name": input.name, "filename": filename, @@ -76,14 +92,15 @@ class UploadPhotoMutation: "created_by_id": info.context.user_id, }, db_session=info.context.db) - return created_photo - @strawberry.type class EditPhotoMutation: - @strawberry_sqlalchemy_input(models.Photo, exclude_fields=["id"], all_optional=True) + @strawberry.input class EditPhotoInput: - pass + 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() @@ -91,7 +108,22 @@ class EditPhotoMutation: query = get_base_query(info.context.user_id) photo = (await info.context.db.scalars(query.filter(models.Photo.id == id))).one() - updated_model = await models.Photo.update(info.context.db, obj=photo, data=input.to_dict()) + data = { + key: getattr(input, key) for key in ('name', 'description', 'is_flight_cover') + if getattr(input, key) is not None + } + + if input.point_of_interest: + data['point_of_interest_id'] = await handle_combobox_save( + info.context.db, + models.PointOfInterest, + input.point_of_interest, + info.context.user_id, + extra_data={ + "description": "" + } + ) + updated_model = await models.Photo.update(info.context.db, obj=photo, data=data) if input.is_flight_cover: # reset other covers @@ -99,7 +131,6 @@ class EditPhotoMutation: update(models.Photo) .filter(models.Photo.flight_id == photo.flight_id) .filter(models.Photo.id != id).values(is_flight_cover=False)) - ) return updated_model @@ -118,7 +149,7 @@ class DeletePhotoMutation: delete_file(f"{base_path}/{photo.filename}") delete_file(f"{base_path}/thumbs/{photo.filename}") except Exception as e: - print("ERROR", e) + print(e) await info.context.db.delete(photo) diff --git a/src/graphql_schema/entities/poi.py b/src/graphql_schema/entities/poi.py index 5ce44fa..1759a7d 100644 --- a/src/graphql_schema/entities/poi.py +++ b/src/graphql_schema/entities/poi.py @@ -7,14 +7,13 @@ from graphql_schema.dataloaders.flight import flight_by_poi_dataloader from graphql_schema.dataloaders.photos import poi_photos_dataloader from graphql_schema.dataloaders.poi import poi_type_dataloader from graphql_schema.entities.helpers.flight import handle_combobox_save -from graphql_schema.entities.photo import Photo from graphql_schema.entities.poi_type import PointOfInterestType from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type, strawberry_sqlalchemy_input from graphql_schema.types import ComboboxInput - if TYPE_CHECKING: from .flight import Flight + from .photo import Photo @strawberry_sqlalchemy_type(models.PointOfInterest) class PointOfInterest: @@ -28,7 +27,7 @@ class PointOfInterest: return await flight_by_poi_dataloader.load(root.id) type: Optional[PointOfInterestType] = strawberry.field(resolver=load_type) - photos: List[Photo] = strawberry.field(resolver=load_photos) + photos: List[Annotated["Photo", strawberry.lazy('.photo')]] = strawberry.field(resolver=load_photos) flights: List[Annotated["Flight", strawberry.lazy('.flight')]] = strawberry.field(resolver=load_flights) @@ -72,7 +71,6 @@ class PointOfInterestQueries: return (await info.context.db.scalars(query)).one() - @strawberry.type class CreatePointOfInterestMutation: @strawberry_sqlalchemy_input(models.PointOfInterest, exclude_fields=['id', 'type_id']) @@ -84,12 +82,13 @@ class CreatePointOfInterestMutation: async def create_point_of_interest(root, info, input: CreatePointOfInterestInput) -> PointOfInterest: input_data = input.to_dict() - input_data['type_id'] = await handle_combobox_save( - info.context.db, - models.PointOfInterestType, - input.type, - info.context.user_id - ) + if input.type: + input_data['type_id'] = await handle_combobox_save( + info.context.db, + models.PointOfInterestType, + input.type, + info.context.user_id + ) return await models.PointOfInterest.create( info.context.db, diff --git a/src/graphql_schema/entities/user.py b/src/graphql_schema/entities/user.py index 68d78d5..c343a4a 100644 --- a/src/graphql_schema/entities/user.py +++ b/src/graphql_schema/entities/user.py @@ -10,7 +10,7 @@ from database import models from database.models import User from decorators.endpoints import authenticated_user_only from decorators.error_logging import error_logging -from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type, strawberry_sqlalchemy_input +from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type from upload_utils import handle_file_upload, delete_file, get_public_url, resize_image @@ -46,22 +46,18 @@ class UserQueries: @authenticated_user_only() @error_logging async def logged_user(root, info) -> User: - user = (await info.context.db.scalars( + return (await info.context.db.scalars( select(models.User).filter_by(id=info.context.user_id) )).one() - print(user) - return user - @strawberry.type class EditUserMutation: - @strawberry_sqlalchemy_input( - models.User, - exclude_fields=['id', 'email', 'avatar_image_filename', 'password_hashed', 'title_image_filename'], - all_optional=True - ) + @strawberry.input class EditUserInput: + name: Optional[str] = None + description: Optional[str] = None + public_username: Optional[str] = None old_password: Optional[str] = None new_password: Optional[str] = None avatar_image: Optional[Upload] = None diff --git a/src/upload_utils.py b/src/upload_utils.py index a8cf28c..7e5d74f 100644 --- a/src/upload_utils.py +++ b/src/upload_utils.py @@ -63,7 +63,6 @@ async def parse_exif_info(path: str, filename: str) -> dict: async def resize_image(path: str, filename: str, new_width: int, quality: int = 90, dest_path: str = None, dest_filename: str = None): - print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") if not dest_path: dest_path = path @@ -79,22 +78,18 @@ async def resize_image(path: str, filename: str, new_width: int, quality: int = image = image.resize((new_width, new_height), Image.LANCZOS) check_directories(dest_path) image.save(f"{dest_path}/{dest_filename}", 'JPEG', quality=quality) - except UnidentifiedImageError as e : + except UnidentifiedImageError as e: pass - print(e) - print("AAA^^^^^^^^^^^^AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") -async def generate_thumbnail(path: str, filename: str, size: Tuple[int, int], quality: int = 90): - try: - image = Image.open(f"{path}/{filename}") - image = image.thumbnail(size, Image.LANCZOS) - check_directories(f"{path}/thumbs/") - image.save(f"{path}/thumbs/{filename}", 'JPEG', quality=quality) - - return await resize_image(path, filename, size, dest_path=f"{path}/thumbs/", dest_filename=filename) - except UnidentifiedImageError: - pass +async def generate_thumbnail(path: str, filename: str, quality: int = 90): + return await resize_image( + path, filename, + new_width=300, + dest_path=f"{path}/thumbs/", + dest_filename=filename, + quality=quality + ) def delete_file(path: str, silent: bool = False):