Ukladani metadat k planum

This commit is contained in:
Michal Kváček
2024-04-30 22:46:40 +02:00
parent c8138860ca
commit a706303155
7 changed files with 160 additions and 9 deletions
@@ -0,0 +1,58 @@
"""add flight plan meta information
Revision ID: 790ba00fc021
Revises: f7ab4bdac987
Create Date: 2024-04-29 16:40:09.829317
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '790ba00fc021'
down_revision = 'f7ab4bdac987'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('flight_plan_has_copilot',
sa.Column('flight_plan_id', sa.Integer(), nullable=False),
sa.Column('copilot_id', sa.Integer(), nullable=False),
sa.Column('token', sa.String(length=64), nullable=True),
sa.ForeignKeyConstraint(['copilot_id'], ['copilot.id'], ),
sa.ForeignKeyConstraint(['flight_plan_id'], ['flight_plan.id'], ),
sa.PrimaryKeyConstraint('flight_plan_id', 'copilot_id'),
sa.UniqueConstraint('token')
)
op.add_column('flight_plan', sa.Column('is_default_name', sa.Boolean(), server_default='0', nullable=False))
op.add_column('flight_plan', sa.Column('is_editable_by_copilot', sa.Boolean(), server_default='0', nullable=False))
op.add_column('flight_plan', sa.Column('planned_speed', sa.Integer(), nullable=False))
op.add_column('flight_plan', sa.Column('planned_takeoff_datetime', sa.DateTime(), nullable=True))
op.add_column('flight_plan', sa.Column('aircraft_id', sa.Integer(), nullable=True))
op.alter_column('flight_plan', 'name',
existing_type=mysql.VARCHAR(length=128),
type_=sa.String(length=256),
existing_nullable=False)
op.create_foreign_key(None, 'flight_plan', 'aircraft', ['aircraft_id'], ['id'])
op.drop_column('point_of_interest_type', 'openaip_id')
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('point_of_interest_type', sa.Column('openaip_id', mysql.VARCHAR(length=32), nullable=False))
op.drop_constraint(None, 'flight_plan', type_='foreignkey')
op.alter_column('flight_plan', 'name',
existing_type=sa.String(length=256),
type_=mysql.VARCHAR(length=128),
existing_nullable=False)
op.drop_column('flight_plan', 'aircraft_id')
op.drop_column('flight_plan', 'planned_takeoff_datetime')
op.drop_column('flight_plan', 'planned_speed')
op.drop_column('flight_plan', 'is_editable_by_copilot')
op.drop_column('flight_plan', 'is_default_name')
op.drop_table('flight_plan_has_copilot')
# ### end Alembic commands ###
+18 -1
View File
@@ -59,6 +59,14 @@ flight_has_copilot = Table(
Column("token", String(64), unique=True)
)
flight_plan_has_copilot = Table(
"flight_plan_has_copilot",
BaseModel.metadata,
Column("flight_plan_id", ForeignKey("flight_plan.id"), primary_key=True),
Column("copilot_id", ForeignKey("copilot.id"), primary_key=True),
Column("token", String(64), unique=True)
)
copilot_has_photo = Table(
"copilot_has_photo",
BaseModel.metadata,
@@ -93,12 +101,20 @@ class FlightPlan(BaseModel):
__tablename__ = "flight_plan"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(128), nullable=False)
name: Mapped[str] = mapped_column(String(256), nullable=False)
is_default_name: Mapped[bool] = mapped_column(Boolean, server_default='0')
is_public: Mapped[bool] = mapped_column(Boolean, server_default='0')
is_editable_by_copilot: Mapped[bool] = mapped_column(Boolean, server_default='0')
planned_speed: Mapped[int] = mapped_column(Integer, nullable=False, default=150)
planned_takeoff_datetime: Mapped[datetime] = mapped_column(DateTime, nullable=True)
aircraft_id: Mapped[int] = mapped_column(Integer, ForeignKey("aircraft.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')
copilots: Mapped[List['Copilot']] = relationship(secondary=flight_plan_has_copilot)
aircraft: Mapped['Aircraft'] = relationship()
markers: Mapped['FlightPlanMarker'] = relationship()
created_by: Mapped['User'] = relationship()
@@ -403,6 +419,7 @@ class Copilot(BaseModel):
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
flights: Mapped[Set['Flight']] = relationship(secondary=flight_has_copilot)
flight_plans: Mapped[Set['FlightPlan']] = relationship(secondary=flight_plan_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])
+17 -1
View File
@@ -20,9 +20,25 @@ flight_copilots_dataloader = DataLoader(
models.flight_has_copilot.c.flight_id == models.Flight.id,
models.flight_has_copilot.c.copilot_id == models.Copilot.id,
],
extra_join=[models.Copilot.flights]).load,
extra_join=[models.Copilot.flights]
).load,
cache=False)
flight_plan_copilots_dataloader = DataLoader(
load_fn=FlightCopilotDataloader(
models.Copilot,
relationship_column=models.FlightPlan.id,
extra_select=[models.flight_plan_has_copilot.c.token],
# filters=[
# models.flight_plan_has_copilot.c.flight_plan_id == models.FlightPlan.id,
# models.flight_plan_has_copilot.c.copilot_id == models.Copilot.id,
# ],
extra_join=[models.Copilot.flight_plans]
).load,
cache=False
)
reporting_points_dataloader = DataLoader(
load_fn=MultiModelsDataloader(
models.ReportingPoint,
@@ -87,7 +87,7 @@ class BaseQueryResolver(BaseResolver):
class BaseMutationResolver(BaseResolver):
async def _get_one(self, db: AsyncSession, id: int, created_by_id: int) -> models.BaseModel:
async def _get_one(self, db: AsyncSession, id: int, created_by_id: int):
query = self.query_builder.get_simple_query(created_by_id=created_by_id).filter(self.model.id == id)
return (await db.scalars(query)).one()
@@ -1,9 +1,17 @@
from sqlalchemy import delete
import asyncio
from sqlalchemy import delete, select
from sqlalchemy.dialects.mysql import insert
from database import models
from database.models import flight_plan_has_copilot
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.flight import handle_aircraft_save
from graphql_schema.entities.types.mutation_input import CreateFlightPlanInput, EditFlightPlanInput
from graphql_schema.entities.types.types import FlightPlan
from utils.str_utils import random_str
class FlightPlanMutationResolver(BaseMutationResolver):
@@ -41,13 +49,55 @@ class FlightPlanMutationResolver(BaseMutationResolver):
await self.save_markers(db, flight_plan, data.markers)
return flight_plan
async def save_copilots(self, db, flight_plan_id: int, copilots: list, user_id: int):
await db.execute(delete(flight_plan_has_copilot).filter_by(flight_plan_id=flight_plan_id))
copilots = await asyncio.gather(*[
handle_combobox_save(db, models.Copilot, copilot, user_id) for copilot in copilots
])
for copilot_id in copilots:
await db.execute(insert(flight_plan_has_copilot).values(
flight_plan_id=flight_plan_id,
copilot_id=copilot_id,
token=random_str(64)
))
async def update(self, id: int, data: EditFlightPlanInput, user_id: int) -> FlightPlan:
input_data = data.to_dict()
async with get_session() as db:
flight_plan = await self._do_update(db, id, input_data)
flight_plan_model = await self._get_one(db, id=id, created_by_id=user_id)
await self.reset_plan_markers(db, flight_plan)
await self.save_markers(db, flight_plan, data.markers)
if data.aircraft is not None:
input_data['aircraft_id'] = await handle_aircraft_save(db, user_id, data.aircraft)
if data.markers is not None:
await self.reset_plan_markers(db, flight_plan_model)
await self.save_markers(db, flight_plan_model, data.markers)
if flight_plan_model.is_default_name:
if data.markers:
markers = data.markers
else:
markers = (await db.scalars(select(models.FlightPlanMarker).filter(models.FlightPlanMarker.flight_plan_id == id))).all()
used_markers = evenly_spaced_elements(markers, 5)
input_data['name'] = " - ".join(m.name for m in used_markers)
if data.copilots is not None:
await self.save_copilots(
db,
flight_plan_id=id,
copilots=data.copilots,
user_id=user_id
)
flight_plan = await self._do_update(db, obj=flight_plan_model, data=input_data)
return flight_plan
def evenly_spaced_elements(lst: list, count: int) -> list:
if count > len(lst):
return lst
interval = (len(lst) - 1) / (count - 1) if count > 1 else 0
return [lst[int(round(i * interval))] for i in range(count)]
@@ -5,6 +5,7 @@ import strawberry
from strawberry.file_uploads import Upload
from database import models
from graphql_schema.entities.types.base import BaseGraphqlInputType
from graphql_schema.entities.types.types import Aircraft
from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_input
@@ -36,7 +37,9 @@ class CreateFlightPlanInput(BaseGraphqlInputType):
@strawberry_sqlalchemy_input(model=models.FlightPlan, exclude_fields=["id", "markers"], all_optional=True)
class EditFlightPlanInput(BaseGraphqlInputType):
markers: list[FlightPlanMarkerInput]
markers: Optional[list[FlightPlanMarkerInput]] = None
copilots: Optional[list[ComboboxInput]] = None
aircraft: Optional[ComboboxInput] = None
@strawberry_sqlalchemy_input(model=models.Copilot, exclude_fields=["id"], all_optional=True)
+8 -1
View File
@@ -14,7 +14,8 @@ from graphql_schema.dataloaders.multi_models import (
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,
photo_copilots_dataloader, photos_aircraft_dataloader, copilots_in_photo_dataloader, flight_plan_markers_dataloader, reporting_points_dataloader
photo_copilots_dataloader, photos_aircraft_dataloader, copilots_in_photo_dataloader, flight_plan_markers_dataloader,
reporting_points_dataloader, flight_plan_copilots_dataloader
)
from graphql_schema.dataloaders.single_model import (
poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader,
@@ -227,12 +228,18 @@ class FlightPlan:
)
return navigation
@authenticated_user_only(raise_when_unauthorized=False, return_value_unauthorized=[])
async def load_copilots(root):
return await flight_plan_copilots_dataloader.load(root.id)
markers: list[FlightPlanMarker] = strawberry.field(
resolver=lambda root: flight_plan_markers_dataloader.load(root.id)
)
track: Optional[list[FlightPlanTrack]] = strawberry.field(
resolver=resolve_track
)
copilots: List[Copilot] = strawberry.field(resolver=load_copilots)
aircraft: Optional[Aircraft] = strawberry.field(resolver=lambda root: aircraft_dataloader.load(root.aircraft_id))
@strawberry_sqlalchemy_type(models.Copilot)