Hromada uprav kvuli ssr renderu a verejne casti

This commit is contained in:
Michal Kváček
2023-12-04 22:45:44 +01:00
parent ecb37fb961
commit 3167ea2f8a
19 changed files with 334 additions and 90 deletions
@@ -0,0 +1,50 @@
"""add photos to aircraft, poi and copilots
Revision ID: 91a9608b509d
Revises: d42172840f6c
Create Date: 2023-11-28 16:32:56.725312
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '91a9608b509d'
down_revision = 'd42172840f6c'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('copilot_has_photo',
sa.Column('copilot_id', sa.Integer(), nullable=False),
sa.Column('photo_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['copilot_id'], ['copilot.id'], ),
sa.ForeignKeyConstraint(['photo_id'], ['photo.id'], ),
sa.PrimaryKeyConstraint('copilot_id', 'photo_id')
)
op.add_column('aircraft', sa.Column('title_photo_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'aircraft', 'photo', ['title_photo_id'], ['id'])
op.add_column('flight', sa.Column('title_photo_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'flight', 'photo', ['title_photo_id'], ['id'])
op.add_column('photo', sa.Column('aircraft_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'photo', 'aircraft', ['aircraft_id'], ['id'])
op.add_column('point_of_interest', sa.Column('title_photo_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'point_of_interest', 'photo', ['title_photo_id'], ['id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'point_of_interest', type_='foreignkey')
op.drop_column('point_of_interest', 'title_photo_id')
op.drop_constraint(None, 'photo', type_='foreignkey')
op.drop_column('photo', 'aircraft_id')
op.drop_constraint(None, 'flight', type_='foreignkey')
op.drop_column('flight', 'title_photo_id')
op.drop_constraint(None, 'aircraft', type_='foreignkey')
op.drop_column('aircraft', 'title_photo_id')
op.drop_table('copilot_has_photo')
# ### end Alembic commands ###
@@ -0,0 +1,28 @@
"""add is_public to aircraft
Revision ID: e5682ac4355c
Revises: 91a9608b509d
Create Date: 2023-12-02 12:14:25.055308
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e5682ac4355c'
down_revision = '91a9608b509d'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('aircraft', sa.Column('is_public', sa.Boolean(), server_default='0', nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('aircraft', 'is_public')
# ### end Alembic commands ###
@@ -0,0 +1,30 @@
"""add title photo to copilot
Revision ID: 022bf6fbcd47
Revises: e5682ac4355c
Create Date: 2023-12-02 13:05:46.267006
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '022bf6fbcd47'
down_revision = 'e5682ac4355c'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('copilot', sa.Column('title_photo_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'copilot', 'photo', ['title_photo_id'], ['id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'copilot', type_='foreignkey')
op.drop_column('copilot', 'title_photo_id')
# ### end Alembic commands ###
+1 -1
View File
@@ -21,7 +21,7 @@ services:
TZ: "Europe/Prague"
API_URL: "http://localhost:8000"
APP_ENV: "development"
ALLOW_CORS_ORIGINS: "http://localhost:9000 http://localhost:9001 http://localhost:3000"
ALLOW_CORS_ORIGINS: "http://localhost:9000 http://localhost:9001 http://localhost:9101 http://localhost:3000"
SENTRY_DSN: "https://184d1d6bd39f4fbb94804ae84e9afdd5@o472821.ingest.sentry.io/5506983"
+29 -11
View File
@@ -57,6 +57,13 @@ flight_has_copilot = Table(
Column("copilot_id", ForeignKey("copilot.id"), primary_key=True),
)
copilot_has_photo = Table(
"copilot_has_photo",
BaseModel.metadata,
Column("copilot_id", ForeignKey("copilot.id"), primary_key=True),
Column("photo_id", ForeignKey("photo.id"), primary_key=True),
)
class Airport(BaseModel):
__tablename__ = "airport"
@@ -72,7 +79,7 @@ class Airport(BaseModel):
created_by_id: Mapped[int] = mapped_column(Integer, ForeignKey('user.id'), nullable=True) # automaticky import nebude mit ID # noqa
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
metars: Mapped['Metar'] = relationship(back_populates="airport")
metars: Mapped['Metar'] = relationship()
created_by: Mapped['User'] = relationship()
@@ -97,6 +104,7 @@ class PointOfInterest(BaseModel):
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(128), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
title_photo_id: Mapped[int] = mapped_column(Integer, ForeignKey('photo.id'), nullable=True)
gps_latitude: Mapped[float] = mapped_column(Float, nullable=True)
gps_longitude: Mapped[float] = mapped_column(Float, nullable=True)
type_id: Mapped[int] = mapped_column(Integer, ForeignKey("point_of_interest_type.id"), nullable=True)
@@ -105,9 +113,9 @@ class PointOfInterest(BaseModel):
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
photos: Mapped[List[Photo]] = relationship()
type: Mapped[PointOfInterestType] = relationship()
created_by: Mapped['User'] = relationship()
title_photo: Mapped['Photo'] = relationship(foreign_keys=[title_photo_id])
class Photo(BaseModel):
@@ -116,7 +124,7 @@ class Photo(BaseModel):
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(128), nullable=False)
filename: Mapped[str] = mapped_column(String(128), nullable=False)
is_flight_cover: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="0")
is_flight_cover: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="0") # TODO: odstranit
description: Mapped[str] = mapped_column(Text, nullable=False)
width: Mapped[int] = mapped_column(Integer, nullable=False)
height: Mapped[int] = mapped_column(Integer, nullable=False)
@@ -125,15 +133,18 @@ class Photo(BaseModel):
gps_longitude: Mapped[float] = mapped_column(Float, nullable=True)
gps_altitude: Mapped[float] = mapped_column(Float, nullable=True)
terrain_elevation: Mapped[float] = mapped_column(Float, nullable=True)
aircraft_id: Mapped[int] = mapped_column(Integer, ForeignKey("aircraft.id"), nullable=True)
point_of_interest_id: Mapped[int] = mapped_column(Integer, ForeignKey("point_of_interest.id"), nullable=True)
flight_id: Mapped[int] = mapped_column(Integer, ForeignKey("flight.id"), nullable=False)
created_by_id: Mapped[int] = mapped_column(Integer, ForeignKey('user.id'))
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
flight: Mapped['Flight'] = relationship(foreign_keys=[flight_id])
point_of_interest: Mapped['PointOfInterest'] = relationship()
point_of_interest: Mapped['PointOfInterest'] = relationship(foreign_keys=[point_of_interest_id])
adjustment: Mapped['PhotoAdjustment'] = relationship(passive_deletes=True)
created_by: Mapped['User'] = relationship()
aircraft: Mapped['Aircraft'] = relationship(foreign_keys=[aircraft_id])
copilots: Mapped[List['Copilot']] = relationship(secondary=copilot_has_photo)
class PhotoAdjustment(BaseModel):
@@ -153,7 +164,6 @@ class PhotoAdjustment(BaseModel):
crop_height: Mapped[float] = mapped_column(Float, nullable=True)
updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
photo: Mapped['Photo'] = relationship()
@@ -162,7 +172,9 @@ class Aircraft(BaseModel):
id: Mapped[int] = mapped_column(primary_key=True)
call_sign: Mapped[str] = mapped_column(String(16), nullable=False)
photo_filename: Mapped[str] = mapped_column(String(128), nullable=True)
photo_filename: Mapped[str] = mapped_column(String(128), nullable=True) # TODO: odstranit
title_photo_id: Mapped[int] = mapped_column(Integer, ForeignKey('photo.id'), nullable=True)
is_public: Mapped[bool] = mapped_column(Boolean, server_default='0')
manufacturer: Mapped[str] = mapped_column(Text, nullable=False, server_default="")
model: Mapped[str] = mapped_column(String(30), nullable=False, server_default="")
seats: Mapped[str] = mapped_column(Integer, nullable=False)
@@ -190,7 +202,7 @@ class Aircraft(BaseModel):
# created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
#
# created_by: Mapped['User'] = relationship()
# aircraft: Mapped['Aircraft'] = relationship(back_populates="notes")
# aircraft: Mapped['Aircraft'] = relationship()
class Organization(BaseModel):
@@ -202,7 +214,7 @@ class Organization(BaseModel):
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
users: Mapped[Set['User']] = relationship(back_populates='organizations', secondary=user_is_in_organization)
users: Mapped[Set['User']] = relationship(secondary=user_is_in_organization)
created_by: Mapped['User'] = relationship()
@@ -261,6 +273,8 @@ class Flight(BaseModel):
name: Mapped[str] = mapped_column(String(128), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
event_id: Mapped[int] = mapped_column(Integer, ForeignKey("event.id"), nullable=True)
title_photo_id: Mapped[int] = mapped_column(Integer, ForeignKey('photo.id'), nullable=True)
takeoff_airport_id: Mapped[int] = mapped_column(Integer, ForeignKey("airport.id"), nullable=False)
landing_airport_id: Mapped[int] = mapped_column(Integer, ForeignKey("airport.id"), nullable=False)
takeoff_datetime: Mapped[datetime] = mapped_column(DateTime, nullable=False)
@@ -285,9 +299,10 @@ class Flight(BaseModel):
track: Mapped['FlightTrack'] = relationship()
event: Mapped['Event'] = relationship()
copilots: Mapped[List['Copilot']] = relationship(secondary=flight_has_copilot)
aircraft: Mapped['Aircraft'] = relationship(back_populates="flights")
aircraft: Mapped['Aircraft'] = relationship()
photos: Mapped[List['Photo']] = relationship(foreign_keys=[Photo.flight_id])
created_by: Mapped['User'] = relationship()
title_photo: Mapped['Photo'] = relationship(foreign_keys=[title_photo_id])
class Copilot(BaseModel):
@@ -295,12 +310,15 @@ class Copilot(BaseModel):
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(128), nullable=False)
title_photo_id: Mapped[int] = mapped_column(Integer, ForeignKey('photo.id'), nullable=True)
created_by_id: Mapped[int] = mapped_column(Integer, ForeignKey('user.id'))
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
flights: Mapped[Set['Flight']] = relationship(secondary=flight_has_copilot)
created_by: Mapped['User'] = relationship()
photos: Mapped[List['Photo']] = relationship(secondary=copilot_has_photo)
title_photo: Mapped['Photo'] = relationship(foreign_keys=[title_photo_id])
class Metar(BaseModel):
@@ -312,7 +330,7 @@ class Metar(BaseModel):
issued_at: Mapped[datetime] = mapped_column(DateTime)
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
airport: Mapped['Airport'] = relationship(back_populates="metars")
airport: Mapped['Airport'] = relationship()
class License(BaseModel):
@@ -325,7 +343,7 @@ class License(BaseModel):
created_by_id: Mapped[int] = mapped_column(Integer, ForeignKey('user.id'))
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
user: Mapped['User'] = relationship(back_populates="licences")
user: Mapped['User'] = relationship()
created_by: Mapped['User'] = relationship()
+1 -6
View File
@@ -23,12 +23,7 @@ class QueryBuilder:
query = query.filter(self.model.deleted.is_(False))
if hasattr(self.model, "created_by_id") and created_by_id:
ownership = [self.model.created_by_id == created_by_id]
if hasattr(self.model, "is_public"):
ownership.append(self.model.is_public.is_(True))
query = query.filter(or_(*ownership))
query = query.filter(self.model.created_by_id == created_by_id)
if order_by:
query = query.order_by(*order_by)
@@ -110,3 +110,32 @@ flight_track_dataloader = DataLoader(
).load,
cache=False
)
copilots_in_photo_dataloader = DataLoader(
load_fn=MultiModelsDataloader(
models.Copilot,
relationship_column=models.copilot_has_photo.c.photo_id,
extra_join=[models.copilot_has_photo],
order_by=[models.Copilot.name]
).load,
cache=False
)
photo_copilots_dataloader = DataLoader(
load_fn=MultiModelsDataloader(
models.Photo,
relationship_column=models.copilot_has_photo.c.copilot_id,
extra_join=[models.copilot_has_photo],
order_by=[models.Photo.exposed_at]
).load,
cache=False
)
photos_aircraft_dataloader = DataLoader(
load_fn=MultiModelsDataloader(
models.Photo,
relationship_column=models.Photo.aircraft_id,
order_by=[models.Photo.exposed_at]
).load,
cache=False
)
@@ -9,6 +9,7 @@ def create_dataloader(model: Type[models.BaseModel], relationship_column=None, f
return DataLoader(load_fn=loader, cache=False)
user_dataloader = create_dataloader(models.User)
airport_dataloader = create_dataloader(models.Airport)
aircraft_dataloader = create_dataloader(models.Aircraft)
event_dataloader = create_dataloader(models.Event)
@@ -18,12 +19,6 @@ poi_dataloader = create_dataloader(models.PointOfInterest)
poi_type_dataloader = create_dataloader(models.PointOfInterestType)
flight_dataloader = create_dataloader(models.Flight)
photo_adjustment_dataloader = create_dataloader(
models.PhotoAdjustment,
relationship_column=models.PhotoAdjustment.photo_id
)
photo_dataloader = create_dataloader(models.Photo)
cover_photo_loader = create_dataloader(
models.Photo,
relationship_column=models.Photo.flight_id,
filters=[models.Photo.is_flight_cover.is_(True)]
models.PhotoAdjustment, relationship_column=models.PhotoAdjustment.photo_id
)
photo_dataloader = create_dataloader(models.Photo)
+9 -4
View File
@@ -1,5 +1,8 @@
from typing import List
from typing import List, Optional
import strawberry
from fastapi import HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from .resolvers.aircraft import AircraftMutationResolver, AircraftQueryResolver
@@ -20,10 +23,12 @@ class AircraftQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def aircraft(root, info, id: int) -> Aircraft:
async def aircraft(root, info, id: int, public: Optional[bool] = False) -> Aircraft:
if not info.context.user_id and not public:
raise HTTPException(HTTP_401_UNAUTHORIZED)
return await AircraftQueryResolver().get_one(
id, user_id=info.context.user_id, organization_ids=info.context.organization_ids
id, user_id=info.context.user_id, organization_ids=info.context.organization_ids, public=public
)
+21 -6
View File
@@ -19,19 +19,34 @@ class FlightQueries:
@strawberry.field()
@error_logging
async def flights(root, info, username: Optional[str] = None) -> List[Flight]:
if not info.context.user_id and not username:
async def flights(
root, info,
username: Optional[str] = None,
public: Optional[bool] = False,
copilot_id: Optional[int] = None,
point_of_interest_id: Optional[int] = None,
aircraft_id: Optional[int] = None,
) -> List[Flight]:
if not info.context.user_id and not public:
raise HTTPException(HTTP_401_UNAUTHORIZED)
return await FlightQueryResolver().get_list(user_id=info.context.user_id, username=username)
return await FlightQueryResolver().get_list(
user_id=info.context.user_id,
username=username,
only_public=public,
copilot_id=copilot_id,
aircraft_id=aircraft_id,
point_of_interest_id=point_of_interest_id
)
@strawberry.field()
@error_logging
async def flight(root, info, id: int, username: Optional[str] = None) -> Flight:
if not info.context.user_id and not username:
async def flight(root, info, id: int, username: Optional[str] = None, public: Optional[bool] = False) -> Flight:
if not info.context.user_id and not public:
raise HTTPException(HTTP_401_UNAUTHORIZED)
return await FlightQueryResolver().get_one(id, user_id=info.context.user_id, username=username)
return await FlightQueryResolver().get_one(id, user_id=info.context.user_id, username=username, public=public)
@strawberry.type
+18 -4
View File
@@ -1,10 +1,10 @@
from typing import List
from typing import List, Optional
import strawberry
from database import models
from decorators.endpoints import authenticated_user_only
from decorators.error_logging import error_logging
from graphql_schema.entities.resolvers.base import BaseQueryResolver
from graphql_schema.entities.resolvers.photo import PhotoMutationResolver
from graphql_schema.entities.resolvers.photo import PhotoMutationResolver, PhotoQueryResolver
from graphql_schema.entities.types.types import Photo
from graphql_schema.entities.types.mutation_input import EditPhotoInput, UploadPhotoInput, AdjustmentInput
@@ -13,8 +13,22 @@ from graphql_schema.entities.types.mutation_input import EditPhotoInput, UploadP
class PhotoQueries:
@strawberry.field()
@error_logging
async def photos(root, info) -> List[Photo]:
return await BaseQueryResolver(Photo, models.Photo).get_list(user_id=info.context.user_id)
async def photos(
root, info,
flight_id: Optional[int] = None,
copilot_id: Optional[int] = None,
point_of_interest_id: Optional[int] = None,
aircraft_id: Optional[int] = None,
public: Optional[bool] = False,
) -> List[Photo]:
return await PhotoQueryResolver().get_list(
public=public,
flight_id=flight_id,
user_id=info.context.user_id,
copilot_id=copilot_id,
aircraft_id=aircraft_id,
point_of_interest_id=point_of_interest_id,
)
@strawberry.field()
@error_logging
+18 -6
View File
@@ -1,5 +1,7 @@
from typing import List
import strawberry
from fastapi import HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED
from database import models
from decorators.endpoints import authenticated_user_only
from database.transaction import get_session
@@ -14,14 +16,24 @@ from graphql_schema.entities.types.mutation_input import CreatePointOfInterestIn
class PointOfInterestQueries:
@strawberry.field()
@error_logging
@authenticated_user_only()
async def points_of_interest(root, info) -> List[PointOfInterest]:
return await BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_list(info.context.user_id)
async def points_of_interest(root, info, public: bool = False) -> List[PointOfInterest]:
if not info.context.user_id and not public:
raise HTTPException(HTTP_401_UNAUTHORIZED)
return await BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_list(
info.context.user_id,
only_public=public
)
@strawberry.field()
@authenticated_user_only()
async def point_of_interest(root, info, id: int) -> PointOfInterest:
return await BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_one(id, info.context.user_id)
async def point_of_interest(root, info, id: int, public: bool = False) -> PointOfInterest:
if not info.context.user_id and not public:
raise HTTPException(HTTP_401_UNAUTHORIZED)
return await BaseQueryResolver(PointOfInterest, models.PointOfInterest).get_one(
id, info.context.user_id,
only_public=public
)
@strawberry.type
@@ -48,9 +48,6 @@ class AircraftMutationResolver(BaseMutationResolver):
async def create_new(self, data: CreateAircraftInput, user_id: int) -> Aircraft:
input_data = data.to_dict()
if data.photo:
input_data['photo_filename'] = await handle_file_upload(data.photo, AIRCRAFT_UPLOAD_DEST_PATH)
if data.organization:
async with get_session() as db:
input_data['organization_id'] = await handle_combobox_save(
@@ -64,13 +61,6 @@ class AircraftMutationResolver(BaseMutationResolver):
async def edit(self, id: int, user_id: int, data: EditAircraftInput) -> Aircraft:
update_data = data.to_dict()
aircraft = await AircraftQueryResolver().get_one(id, user_id)
if data.photo:
if aircraft.photo_filename:
delete_file(AIRCRAFT_UPLOAD_DEST_PATH + "/" + aircraft.photo_filename, silent=True)
update_data['photo_filename'] = await handle_file_upload(data.photo, AIRCRAFT_UPLOAD_DEST_PATH)
async with get_session() as db:
if data.organization:
update_data['organization_id'] = await handle_combobox_save(
@@ -46,6 +46,9 @@ class BaseQueryResolver(BaseResolver):
if only_public and hasattr(self.model, "is_public"):
query = query.filter(self.model.is_public.is_(True))
if kwargs:
query = query.filter_by(**kwargs)
return query
async def get_list(self, user_id: Optional[int] = None, **kwargs) -> List[GQL_TYPE]:
@@ -16,12 +16,13 @@ class CopilotQueryResolver(BaseQueryResolver):
only_public: Optional[bool] = False,
*args, **kwargs
):
pilot_username = kwargs.pop("pilot_username", None)
query = super().get_query(user_id, object_id, order_by, only_public, *args, **kwargs)
if kwargs.get("pilot_username"):
if pilot_username:
query = (
query.join(models.Copilot.created_by)
.filter(models.User.public_username == kwargs['pilot_username'])
.filter(models.User.public_username == pilot_username)
)
return query
@@ -21,17 +21,38 @@ class FlightQueryResolver(BaseQueryResolver):
def __init__(self):
super().__init__(graphql_type=Flight, model=models.Flight)
def get_query(self, user_id: int, object_id: Optional[int] = None, *args, **kwargs):
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
only_public: Optional[bool] = False,
*args,
**kwargs
):
query = super().get_query(
user_id, object_id,
order_by=[models.Flight.takeoff_datetime.desc()],
only_public=not bool(user_id)
only_public=only_public
)
if kwargs.get("aircraft_id"):
query = query.filter(models.Flight.aircraft_id == kwargs['aircraft_id'])
if kwargs.get("copilot_id"):
query = (
query.join(models.flight_has_copilot)
.filter(models.flight_has_copilot.c.copilot_id == kwargs['copilot_id'])
)
if kwargs.get("point_of_interest_id"):
query = (
query.join(models.Flight.track)
.filter(models.FlightTrack.point_of_interest_id == kwargs["point_of_interest_id"])
)
if kwargs.get('username'):
query = (
query
.join(models.Flight.created_by)
query.join(models.Flight.created_by)
.filter(models.User.public_username == kwargs['username'])
)
+46 -15
View File
@@ -1,15 +1,16 @@
import os
import shutil
from typing import Type, Optional, List
from PIL import Image
from pydantic import BaseModel
from sqlalchemy import update, delete
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete, insert
from background_jobs.elevation import add_terrain_elevation_to_photo
from background_jobs.photo import generate_thumbnail, resize_photo
from database import models
from database.transaction import get_session
from graphql_schema.entities.helpers.combobox import handle_combobox_save
from graphql_schema.entities.resolvers.base import BaseMutationResolver
from graphql_schema.entities.resolvers.base import BaseMutationResolver, BaseQueryResolver, GQL_TYPE
from graphql_schema.entities.types.mutation_input import EditPhotoInput, UploadPhotoInput, AdjustmentInput
from graphql_schema.entities.types.types import Photo
from paths import get_photo_basepath
@@ -18,6 +19,40 @@ from utils.image import PhotoEditor, parse_exif_info
from utils.upload import handle_file_upload
class PhotoQueryResolver(BaseQueryResolver):
def __init__(self):
super().__init__(Photo, models.Photo)
def get_query(
self,
user_id: Optional[int] = None,
object_id: Optional[int] = None,
order_by: Optional[list] = None,
only_public: Optional[bool] = False,
*args, **kwargs
):
query = super().get_query(
user_id, object_id, order_by,
aircraft_id=kwargs.get("aircraft_id"),
point_of_interest_id=kwargs.get("point_of_interest_id")
)
if kwargs.get("public"):
query = (
query.join(models.Flight, onclause=models.Photo.flight_id == models.Flight.id)
.filter(models.Flight.is_public.is_(True))
)
if kwargs.get("copilot_id"):
query = (
query.join(models.copilot_has_photo, )
.filter(models.copilot_has_photo.c.copilot_id == kwargs['copilot_id'])
)
return query
class PhotoDetailInfo(BaseModel):
filename: str
original_filename: str
@@ -29,15 +64,6 @@ class PhotoMutationResolver(BaseMutationResolver):
def __init__(self):
super().__init__(Photo, models.Photo)
@staticmethod
async def _reset_flight_cover(db: AsyncSession, flight_id: int, ignored_photo_id: int):
(await db.execute(
update(models.Photo)
.filter(models.Photo.flight_id == flight_id)
.filter(models.Photo.id != ignored_photo_id)
.values(is_flight_cover=False))
)
@staticmethod
def _copy_original(path: str, filename: str):
original_filename = "_original_" + filename
@@ -105,9 +131,14 @@ class PhotoMutationResolver(BaseMutationResolver):
extra_data={"description": ""}
)
if input.is_flight_cover:
# reset other covers
await self._reset_flight_cover(db, photo.flight_id, id)
if input.is_aircraft:
flight = (await db.scalars(select(models.Flight).filter(models.Flight.id == photo.flight_id))).one()
data['aircraft_id'] = flight.aircraft_id
if input.copilots is not None:
await db.execute(delete(models.copilot_has_photo).filter_by(photo_id=id))
for copilot in input.copilots:
await db.execute(insert(models.copilot_has_photo).values(photo_id=id, copilot_id=copilot.id))
return await self._do_update(db, obj=photo, data=data)
@@ -29,7 +29,7 @@ class CreatePointOfInterestInput(BaseGraphqlInputType):
type: Optional[ComboboxInput] = None
@strawberry_sqlalchemy_input(models.PointOfInterest, exclude_fields=['id', 'type_id'])
@strawberry_sqlalchemy_input(models.PointOfInterest, exclude_fields=['id', 'type_id'], all_optional=True)
class EditPointOfInterestInput(BaseGraphqlInputType):
type: Optional[ComboboxInput] = None
@@ -68,11 +68,12 @@ class EditPhotoInput:
name: Optional[str] = None
description: Optional[str] = None
point_of_interest: Optional[ComboboxInput] = None
is_flight_cover: Optional[bool] = None
is_aircraft: Optional[bool] = None
copilots: Optional[List[ComboboxInput]] = None
def to_dict(self):
return {
key: getattr(self, key) for key in ('name', 'description', 'is_flight_cover')
key: getattr(self, key) for key in ('name', 'description')
if getattr(self, key) is not None
}
@@ -128,11 +129,9 @@ class TrackItemInput:
@strawberry_sqlalchemy_input(models.Aircraft, exclude_fields=['id', 'photo_filename'])
class CreateAircraftInput(BaseGraphqlInputType):
photo: Optional[Upload]
organization: Optional[ComboboxInput] = None
@strawberry_sqlalchemy_input(models.Aircraft, exclude_fields=['photo_filename'])
@strawberry_sqlalchemy_input(models.Aircraft, exclude_fields=['photo_filename'], all_optional=True)
class EditAircraftInput(BaseGraphqlInputType):
photo: Optional[Upload]
organization: Optional[ComboboxInput] = None
+15 -7
View File
@@ -10,12 +10,12 @@ 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, public_flights_by_event_dataloader, public_flights_by_copilot_dataloader
flights_by_copilot_dataloader, public_flights_by_event_dataloader, public_flights_by_copilot_dataloader, photo_copilots_dataloader, photos_aircraft_dataloader, copilots_in_photo_dataloader
)
from graphql_schema.dataloaders.single_model import (
poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader, cover_photo_loader,
poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader,
airport_weather_info_loader, organizations_dataloader, flight_dataloader, photo_adjustment_dataloader,
photo_dataloader
photo_dataloader, user_dataloader
)
from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_type
from paths import (
@@ -76,6 +76,7 @@ class PointOfInterest:
type: Optional[PointOfInterestType] = strawberry.field(resolver=lambda root: poi_type_dataloader.load(root.type_id))
photos: List[Photo] = strawberry.field(resolver=lambda root: poi_photos_dataloader.load(root.id))
flights: List[Flight] = strawberry.field(resolver=lambda root: flight_by_poi_dataloader.load(root.id))
title_photo: Optional[Photo] = strawberry.field(resolver=lambda root: photo_dataloader.load(root.title_photo_id))
@strawberry_sqlalchemy_type(models.Photo)
@@ -85,6 +86,8 @@ class Photo:
point_of_interest: Optional[PointOfInterest] = strawberry.field(
resolver=lambda root: poi_dataloader.load(root.point_of_interest_id)
)
copilots: List[Copilot] = strawberry.field(resolver=lambda root: copilots_in_photo_dataloader.load(root.id))
is_aircraft: bool = strawberry.field(resolver=lambda root: bool(root.aircraft_id))
flight: Flight = strawberry.field(resolver=lambda root: flight_dataloader.load(root.flight_id))
adjustment: Optional[PhotoAdjustment] = strawberry.field(
resolver=lambda root: photo_adjustment_dataloader.load(root.id)
@@ -123,12 +126,13 @@ class Flight:
async def load_event(root):
return await event_dataloader.load(root.event_id)
pilot: User = strawberry.field(resolver=lambda root: user_dataloader.load(root.created_by_id))
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))
cover_photo: Optional[Photo] = strawberry.field(resolver=lambda root: cover_photo_loader.load(root.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(
resolver=lambda root: airport_weather_info_loader.load(root.takeoff_weather_info_id)
@@ -153,17 +157,21 @@ class Copilot:
return await dataloader.load(root.id)
flights: List[Flight] = strawberry.field(resolver=resolve_flights)
photos: List[Photo] = strawberry.field(resolver=lambda root: photo_copilots_dataloader.load(root.id))
title_photo: Optional[Photo] = strawberry.field(resolver=lambda root: photo_dataloader.load(root.title_photo_id))
@strawberry_sqlalchemy_type(models.Aircraft)
class Aircraft:
photo_url: Optional[str] = strawberry.field(
resolver=lambda root: get_public_url(f"aircrafts/{root.photo_filename}") if root.photo_filename else None
)
# photo_url: Optional[str] = strawberry.field(
# resolver=lambda root: get_public_url(f"aircrafts/{root.photo_filename}") if root.photo_filename else None
# )
flights: List[Flight] = strawberry.field(resolver=lambda root: flights_by_aircraft_dataloader.load(root.id))
organization: Optional[Organization] = strawberry.field(
resolver=lambda root: organizations_dataloader.load(root.organization_id)
)
photos: List[Photo] = strawberry.field(resolver=lambda root: photos_aircraft_dataloader.load(root.id))
title_photo: Optional[Photo] = strawberry.field(resolver=lambda root: photo_dataloader.load(root.title_photo_id))
@strawberry_sqlalchemy_type(models.Organization)