Hromada uprav kvuli ssr renderu a verejne casti
This commit is contained in:
+50
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'])
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user