Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62e46f37ce | ||
|
|
a706303155 | ||
|
|
c8138860ca | ||
|
|
c2b066d371 | ||
|
|
cee5de003f | ||
|
|
5f0a1da052 | ||
|
|
63e2eed8c2 | ||
|
|
01f8a616bf | ||
|
|
24825108a7 |
@@ -0,0 +1,38 @@
|
|||||||
|
"""add flight_plan table
|
||||||
|
|
||||||
|
Revision ID: 7a3738a805c5
|
||||||
|
Revises: aba4c3876b28
|
||||||
|
Create Date: 2024-03-21 16:42:08.428875
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '7a3738a805c5'
|
||||||
|
down_revision = 'aba4c3876b28'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('flight_plan',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('markers', mysql.JSON(), nullable=False),
|
||||||
|
sa.Column('is_public', sa.Boolean(), server_default='0', nullable=False),
|
||||||
|
sa.Column('created_by_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
|
||||||
|
sa.Column('deleted', sa.Boolean(), server_default='0', nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('flight_plan')
|
||||||
|
# ### end Alembic commands ###
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
"""make markers in flight planner separate table
|
||||||
|
|
||||||
|
Revision ID: de70fc471699
|
||||||
|
Revises: 7a3738a805c5
|
||||||
|
Create Date: 2024-03-24 21:55:12.307588
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'de70fc471699'
|
||||||
|
down_revision = '7a3738a805c5'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('flight_plan_marker',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('flight_plan_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('gps_latitude', sa.Float(), nullable=True),
|
||||||
|
sa.Column('gps_longitude', sa.Float(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['flight_plan_id'], ['flight_plan.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.drop_column('flight_plan', 'markers')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('flight_plan', sa.Column('markers', mysql.LONGTEXT(charset='utf8mb4', collation='utf8mb4_bin'), nullable=False))
|
||||||
|
op.drop_table('flight_plan_marker')
|
||||||
|
# ### end Alembic commands ###
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
"""add type and position to flight_plan_marker
|
||||||
|
|
||||||
|
Revision ID: 70152c47cb49
|
||||||
|
Revises: de70fc471699
|
||||||
|
Create Date: 2024-03-24 22:00:35.234920
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '70152c47cb49'
|
||||||
|
down_revision = 'de70fc471699'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('flight_plan_marker', sa.Column('type', sa.String(length=64), nullable=False))
|
||||||
|
op.add_column('flight_plan_marker', sa.Column('position', sa.Integer(), nullable=False))
|
||||||
|
op.alter_column('flight_plan_marker', 'gps_latitude',
|
||||||
|
existing_type=mysql.FLOAT(),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('flight_plan_marker', 'gps_longitude',
|
||||||
|
existing_type=mysql.FLOAT(),
|
||||||
|
nullable=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('flight_plan_marker', 'gps_longitude',
|
||||||
|
existing_type=mysql.FLOAT(),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('flight_plan_marker', 'gps_latitude',
|
||||||
|
existing_type=mysql.FLOAT(),
|
||||||
|
nullable=True)
|
||||||
|
op.drop_column('flight_plan_marker', 'position')
|
||||||
|
op.drop_column('flight_plan_marker', 'type')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
"""reworked airspace and airport db structure
|
||||||
|
|
||||||
|
Revision ID: cbdb018c5490
|
||||||
|
Revises: 70152c47cb49
|
||||||
|
Create Date: 2024-04-21 22:32:50.058166
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'cbdb018c5490'
|
||||||
|
down_revision = '70152c47cb49'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('airspace',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('country', sa.String(length=8), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=128), nullable=False),
|
||||||
|
sa.Column('type', sa.String(length=8), nullable=False),
|
||||||
|
sa.Column('lower_limit', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('upper_limit', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('center', mysql.JSON(), nullable=False),
|
||||||
|
sa.Column('radius', sa.Float(), nullable=True),
|
||||||
|
sa.Column('bounds', mysql.JSON(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_airspace_country'), 'airspace', ['country'], unique=False)
|
||||||
|
op.create_index(op.f('ix_airspace_type'), 'airspace', ['type'], unique=False)
|
||||||
|
op.create_table('frequency',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('airport_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('airspace_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('frequency', sa.String(length=7), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=64), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['airport_id'], ['airport.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['airspace_id'], ['airspace.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('runway',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('airport_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('heading', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('designator', sa.String(length=4), nullable=False),
|
||||||
|
sa.Column('surface', sa.Enum('asphalt', 'concrete', 'grass', 'other'), server_default='other', nullable=False),
|
||||||
|
sa.Column('length', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('width', sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['airport_id'], ['airport.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.drop_table('metar')
|
||||||
|
op.drop_table('license')
|
||||||
|
op.drop_index('ix_airspace_item_country', table_name='airspace_item')
|
||||||
|
op.drop_index('ix_airspace_item_type', table_name='airspace_item')
|
||||||
|
op.drop_index('name', table_name='airspace_item')
|
||||||
|
op.drop_table('airspace_item')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('airspace_item',
|
||||||
|
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('country', mysql.VARCHAR(length=8), nullable=False),
|
||||||
|
sa.Column('name', mysql.VARCHAR(length=128), nullable=False),
|
||||||
|
sa.Column('type', mysql.VARCHAR(length=8), nullable=False),
|
||||||
|
sa.Column('lower_limit', mysql.VARCHAR(length=32), nullable=False),
|
||||||
|
sa.Column('upper_limit', mysql.VARCHAR(length=32), nullable=False),
|
||||||
|
sa.Column('center', mysql.LONGTEXT(charset='utf8mb4', collation='utf8mb4_bin'), nullable=False),
|
||||||
|
sa.Column('radius', mysql.FLOAT(), nullable=True),
|
||||||
|
sa.Column('bounds', mysql.LONGTEXT(charset='utf8mb4', collation='utf8mb4_bin'), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
mysql_collate='utf8mb4_general_ci',
|
||||||
|
mysql_default_charset='utf8mb4',
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
||||||
|
op.create_index('name', 'airspace_item', ['name'], unique=True)
|
||||||
|
op.create_index('ix_airspace_item_type', 'airspace_item', ['type'], unique=False)
|
||||||
|
op.create_index('ix_airspace_item_country', 'airspace_item', ['country'], unique=False)
|
||||||
|
op.create_table('license',
|
||||||
|
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', mysql.VARCHAR(length=128), nullable=False),
|
||||||
|
sa.Column('number', mysql.VARCHAR(length=30), nullable=False),
|
||||||
|
sa.Column('valid_until', mysql.DATETIME(), nullable=False),
|
||||||
|
sa.Column('created_by_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('created_at', mysql.DATETIME(), server_default=sa.text('current_timestamp()'), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], name='license_ibfk_1'),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
mysql_collate='utf8mb4_general_ci',
|
||||||
|
mysql_default_charset='utf8mb4',
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
||||||
|
op.create_table('metar',
|
||||||
|
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('airport_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('metar', mysql.TEXT(), nullable=False),
|
||||||
|
sa.Column('issued_at', mysql.DATETIME(), nullable=False),
|
||||||
|
sa.Column('deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), autoincrement=False, nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['airport_id'], ['airport.id'], name='metar_ibfk_1'),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
mysql_collate='utf8mb4_general_ci',
|
||||||
|
mysql_default_charset='utf8mb4',
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
||||||
|
op.drop_table('runway')
|
||||||
|
op.drop_table('frequency')
|
||||||
|
op.drop_index(op.f('ix_airspace_type'), table_name='airspace')
|
||||||
|
op.drop_index(op.f('ix_airspace_country'), table_name='airspace')
|
||||||
|
op.drop_table('airspace')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""remove old airspace columns, add altitude unit
|
||||||
|
|
||||||
|
Revision ID: e07a5c0f1dcc
|
||||||
|
Revises: cbdb018c5490
|
||||||
|
Create Date: 2024-04-22 23:42:52.140389
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'e07a5c0f1dcc'
|
||||||
|
down_revision = 'cbdb018c5490'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('airspace', sa.Column('altitude_unit', sa.String(length=8), nullable=False))
|
||||||
|
op.drop_column('airspace', 'center')
|
||||||
|
op.drop_column('airspace', 'radius')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('airspace', sa.Column('radius', mysql.FLOAT(), nullable=True))
|
||||||
|
op.add_column('airspace', sa.Column('center', mysql.LONGTEXT(charset='utf8mb4', collation='utf8mb4_bin'), nullable=False))
|
||||||
|
op.drop_column('airspace', 'altitude_unit')
|
||||||
|
# ### end Alembic commands ###
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
"""add alt unit for lower/upper limits in airspaces
|
||||||
|
|
||||||
|
Revision ID: 2012eca075c7
|
||||||
|
Revises: e07a5c0f1dcc
|
||||||
|
Create Date: 2024-04-22 23:49:17.725111
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '2012eca075c7'
|
||||||
|
down_revision = 'e07a5c0f1dcc'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('airspace', sa.Column('lower_alt_unit', sa.String(length=8), nullable=False))
|
||||||
|
op.add_column('airspace', sa.Column('upper_alt_unit', sa.String(length=8), nullable=False))
|
||||||
|
op.drop_column('airspace', 'altitude_unit')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('airspace', sa.Column('altitude_unit', mysql.VARCHAR(length=8), nullable=False))
|
||||||
|
op.drop_column('airspace', 'upper_alt_unit')
|
||||||
|
op.drop_column('airspace', 'lower_alt_unit')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""add table for reporting points
|
||||||
|
|
||||||
|
Revision ID: 03ba4dfea911
|
||||||
|
Revises: 2012eca075c7
|
||||||
|
Create Date: 2024-04-23 21:39:18.254033
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '03ba4dfea911'
|
||||||
|
down_revision = '2012eca075c7'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('reporting_point',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('openaip_id', sa.String(length=32), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=64), nullable=False),
|
||||||
|
sa.Column('gps_latitude', sa.Float(), nullable=True),
|
||||||
|
sa.Column('gps_longitude', sa.Float(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('airport_has_reporting_point',
|
||||||
|
sa.Column('airport_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('reporting_point_id', sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['airport_id'], ['airport.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['reporting_point_id'], ['reporting_point.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('airport_id', 'reporting_point_id')
|
||||||
|
)
|
||||||
|
op.add_column('airport', sa.Column('openaip_id', sa.String(length=32), nullable=False))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('airport', 'openaip_id')
|
||||||
|
op.drop_table('airport_has_reporting_point')
|
||||||
|
op.drop_table('reporting_point')
|
||||||
|
# ### end Alembic commands ###
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
"""remove obsolete airport columns
|
||||||
|
|
||||||
|
Revision ID: f7ab4bdac987
|
||||||
|
Revises: 03ba4dfea911
|
||||||
|
Create Date: 2024-04-24 06:24:48.219726
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'f7ab4bdac987'
|
||||||
|
down_revision = '03ba4dfea911'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('airport', 'deleted')
|
||||||
|
op.drop_column('airport', 'is_public')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('airport', sa.Column('is_public', mysql.TINYINT(display_width=1), server_default=sa.text('0'), autoincrement=False, nullable=False))
|
||||||
|
op.add_column('airport', sa.Column('deleted', mysql.TINYINT(display_width=1), server_default=sa.text('0'), autoincrement=False, nullable=False))
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
"""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'])
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
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 ###
|
||||||
@@ -7,7 +7,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: "always"
|
restart: "always"
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- ../.env
|
||||||
environment:
|
environment:
|
||||||
SENTRY_DSN: "https://184d1d6bd39f4fbb94804ae84e9afdd5@o472821.ingest.sentry.io/5506983"
|
SENTRY_DSN: "https://184d1d6bd39f4fbb94804ae84e9afdd5@o472821.ingest.sentry.io/5506983"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+4
-9
@@ -3,10 +3,8 @@ services:
|
|||||||
image: mariadb:10.11
|
image: mariadb:10.11
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
environment:
|
env_file:
|
||||||
- MYSQL_USER=root
|
- .env
|
||||||
- MYSQL_ROOT_PASSWORD=root
|
|
||||||
- MYSQL_DATABASE=ull_tracker
|
|
||||||
volumes:
|
volumes:
|
||||||
# - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
# - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||||
- db-data:/var/lib/mysql:rw
|
- db-data:/var/lib/mysql:rw
|
||||||
@@ -37,11 +35,8 @@ services:
|
|||||||
scrapy:
|
scrapy:
|
||||||
build:
|
build:
|
||||||
context: ../crawlers
|
context: ../crawlers
|
||||||
environment:
|
env_file:
|
||||||
- MYSQL_USER=root
|
- .env
|
||||||
- MYSQL_PASSWORD=root
|
|
||||||
- MYSQL_DATABASE=ull_tracker
|
|
||||||
- MYSQL_HOST=db
|
|
||||||
volumes:
|
volumes:
|
||||||
- db-data:/var/lib/mysql:rw
|
- db-data:/var/lib/mysql:rw
|
||||||
- ../crawlers:/app
|
- ../crawlers:/app
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from paths import FLIGHT_GPX_TRACK_PATH
|
|||||||
|
|
||||||
async def add_terrain_elevation_to_flight(flight_id: int, gpx_filename: str):
|
async def add_terrain_elevation_to_flight(flight_id: int, gpx_filename: str):
|
||||||
gpx_parser = GPXParser(f"{FLIGHT_GPX_TRACK_PATH}/{gpx_filename}")
|
gpx_parser = GPXParser(f"{FLIGHT_GPX_TRACK_PATH}/{gpx_filename}")
|
||||||
coordinates = await gpx_parser.get_coordinates()
|
coordinates = await gpx_parser.get_coordinates_all()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
elevation = await elevation_api.get_elevation_for_points(coordinates)
|
elevation = await elevation_api.get_elevation_for_points(coordinates)
|
||||||
|
|||||||
+87
-34
@@ -59,6 +59,14 @@ flight_has_copilot = Table(
|
|||||||
Column("token", String(64), unique=True)
|
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 = Table(
|
||||||
"copilot_has_photo",
|
"copilot_has_photo",
|
||||||
BaseModel.metadata,
|
BaseModel.metadata,
|
||||||
@@ -67,40 +75,111 @@ copilot_has_photo = Table(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AirspaceItem(BaseModel):
|
airport_has_reporting_point = Table(
|
||||||
__tablename__ = "airspace_item"
|
"airport_has_reporting_point",
|
||||||
|
BaseModel.metadata,
|
||||||
|
Column("airport_id", ForeignKey("airport.id"), primary_key=True),
|
||||||
|
Column("reporting_point_id", ForeignKey("reporting_point.id"), primary_key=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Airspace(BaseModel):
|
||||||
|
__tablename__ = "airspace"
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
country: Mapped[str] = mapped_column(String(8), nullable=False, index=True)
|
country: Mapped[str] = mapped_column(String(8), nullable=False, index=True)
|
||||||
name: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
|
name: Mapped[str] = mapped_column(String(128), nullable=False, unique=True)
|
||||||
type: Mapped[str] = mapped_column(String(8), nullable=False, index=True)
|
type: Mapped[str] = mapped_column(String(8), nullable=False, index=True)
|
||||||
lower_limit: Mapped[str] = mapped_column(String(32), nullable=False)
|
lower_limit: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||||
|
lower_alt_unit: Mapped[str] = mapped_column(String(8), nullable=False)
|
||||||
upper_limit: Mapped[str] = mapped_column(String(32), nullable=False)
|
upper_limit: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||||
center: Mapped[List[dict]] = mapped_column(JSON())
|
upper_alt_unit: Mapped[str] = mapped_column(String(8), nullable=False)
|
||||||
radius: Mapped[float] = mapped_column(Float, nullable=True)
|
|
||||||
bounds: Mapped[list[tuple[float, float]]] = mapped_column(JSON())
|
bounds: Mapped[list[tuple[float, float]]] = mapped_column(JSON())
|
||||||
|
|
||||||
|
|
||||||
|
class FlightPlan(BaseModel):
|
||||||
|
__tablename__ = "flight_plan"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
class FlightPlanMarker(BaseModel):
|
||||||
|
__tablename__ = "flight_plan_marker"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
flight_plan_id: Mapped[int] = mapped_column(Integer, ForeignKey('flight_plan.id'))
|
||||||
|
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||||
|
type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||||
|
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||||
|
gps_latitude: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
gps_longitude: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Airport(BaseModel):
|
class Airport(BaseModel):
|
||||||
__tablename__ = "airport"
|
__tablename__ = "airport"
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||||
|
openaip_id: Mapped[str] = mapped_column(String(32), nullable=False, unique=True)
|
||||||
icao_code: Mapped[str] = mapped_column(String(8), nullable=False)
|
icao_code: Mapped[str] = mapped_column(String(8), nullable=False)
|
||||||
gps_latitude: Mapped[float] = mapped_column(Float, nullable=True)
|
gps_latitude: Mapped[float] = mapped_column(Float, nullable=True)
|
||||||
gps_longitude: Mapped[float] = mapped_column(Float, nullable=True)
|
gps_longitude: Mapped[float] = mapped_column(Float, nullable=True)
|
||||||
elevation: Mapped[int] = mapped_column(Integer, nullable=True)
|
elevation: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||||
airport_type: Mapped[str] = mapped_column(Enum("airport", "ull", "heliport"), nullable=False, server_default='airport') # noqa
|
airport_type: Mapped[str] = mapped_column(Enum("airport", "ull", "heliport"), nullable=False, server_default='airport') # noqa
|
||||||
use_in_gpx_guess: Mapped[bool] = mapped_column(Boolean, server_default='1')
|
use_in_gpx_guess: Mapped[bool] = mapped_column(Boolean, server_default='1')
|
||||||
is_public: Mapped[bool] = mapped_column(Boolean, server_default='0')
|
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
|
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
|
||||||
created_by_id: Mapped[int] = mapped_column(Integer, ForeignKey('user.id'), nullable=True) # automaticky import nebude mit ID # noqa
|
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()
|
|
||||||
created_by: Mapped['User'] = relationship()
|
created_by: Mapped['User'] = relationship()
|
||||||
|
|
||||||
|
|
||||||
|
class Runway(BaseModel):
|
||||||
|
__tablename__ = "runway"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
airport_id: Mapped[int] = mapped_column(Integer, ForeignKey('airport.id'))
|
||||||
|
heading: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
designator: Mapped[str] = mapped_column(String(4), nullable=False)
|
||||||
|
surface: Mapped[str] = mapped_column(Enum("asphalt", "concrete", "grass", "other"), nullable=False, server_default="other") # noqa
|
||||||
|
length: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||||
|
width: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Frequency(BaseModel):
|
||||||
|
__tablename__ = "frequency"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
airport_id: Mapped[int] = mapped_column(Integer, ForeignKey('airport.id'), nullable=True)
|
||||||
|
airspace_id: Mapped[int] = mapped_column(Integer, ForeignKey('airspace.id'), nullable=True)
|
||||||
|
frequency: Mapped[str] = mapped_column(String(7), nullable=False)
|
||||||
|
name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportingPoint(BaseModel):
|
||||||
|
__tablename__ = "reporting_point"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
openaip_id: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||||
|
name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||||
|
gps_latitude: Mapped[float] = mapped_column(Float, nullable=True)
|
||||||
|
gps_longitude: Mapped[float] = mapped_column(Float, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
class PointOfInterestType(BaseModel):
|
class PointOfInterestType(BaseModel):
|
||||||
__tablename__ = "point_of_interest_type"
|
__tablename__ = "point_of_interest_type"
|
||||||
|
|
||||||
@@ -340,37 +419,12 @@ class Copilot(BaseModel):
|
|||||||
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
|
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
|
||||||
|
|
||||||
flights: Mapped[Set['Flight']] = relationship(secondary=flight_has_copilot)
|
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()
|
created_by: Mapped['User'] = relationship()
|
||||||
photos: Mapped[List['Photo']] = relationship(secondary=copilot_has_photo)
|
photos: Mapped[List['Photo']] = relationship(secondary=copilot_has_photo)
|
||||||
title_photo: Mapped['Photo'] = relationship(foreign_keys=[title_photo_id])
|
title_photo: Mapped['Photo'] = relationship(foreign_keys=[title_photo_id])
|
||||||
|
|
||||||
|
|
||||||
class Metar(BaseModel):
|
|
||||||
__tablename__ = "metar"
|
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
|
||||||
airport_id: Mapped[int] = mapped_column(Integer, ForeignKey('airport.id'))
|
|
||||||
metar: Mapped[str] = mapped_column(Text, nullable=False)
|
|
||||||
issued_at: Mapped[datetime] = mapped_column(DateTime)
|
|
||||||
deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='0')
|
|
||||||
|
|
||||||
airport: Mapped['Airport'] = relationship()
|
|
||||||
|
|
||||||
|
|
||||||
class License(BaseModel):
|
|
||||||
__tablename__ = "license"
|
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
|
||||||
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
||||||
number: Mapped[str] = mapped_column(String(30), nullable=False)
|
|
||||||
valid_until: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
|
||||||
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()
|
|
||||||
created_by: Mapped['User'] = relationship()
|
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel):
|
class User(BaseModel):
|
||||||
__tablename__ = "user"
|
__tablename__ = "user"
|
||||||
excluded_columns_in_dict = ('password_hashed',)
|
excluded_columns_in_dict = ('password_hashed',)
|
||||||
@@ -385,7 +439,6 @@ class User(BaseModel):
|
|||||||
password_hashed: Mapped[str] = mapped_column(String(60), nullable=False)
|
password_hashed: Mapped[str] = mapped_column(String(60), nullable=False)
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
|
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
|
||||||
|
|
||||||
licences: Mapped[Set['License']] = relationship()
|
|
||||||
flights: Mapped[Set['Flight']] = relationship()
|
flights: Mapped[Set['Flight']] = relationship()
|
||||||
organizations: Mapped[Set['Organization']] = relationship(secondary=user_is_in_organization)
|
organizations: Mapped[Set['Organization']] = relationship(secondary=user_is_in_organization)
|
||||||
|
|
||||||
|
|||||||
Vendored
+34
@@ -0,0 +1,34 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
EARTH_RADIUS = 6378.1 # Radius of the Earth
|
||||||
|
|
||||||
|
|
||||||
|
def get_bearing(center_point: tuple[float, float], end_point: tuple[float, float]):
|
||||||
|
lat3 = math.radians(end_point[0])
|
||||||
|
long3 = math.radians(end_point[1])
|
||||||
|
lat1 = math.radians(center_point[0])
|
||||||
|
long1 = math.radians(center_point[1])
|
||||||
|
|
||||||
|
dLon = long3 - long1
|
||||||
|
|
||||||
|
X = math.cos(lat3) * math.sin(dLon)
|
||||||
|
Y = math.cos(lat1) * math.sin(lat3) - math.sin(lat1) * math.cos(lat3) * math.cos(dLon)
|
||||||
|
|
||||||
|
end_brng = math.atan2(X, Y)
|
||||||
|
|
||||||
|
return end_brng
|
||||||
|
|
||||||
|
|
||||||
|
def get_distance(a: tuple[float, float], b: tuple[float, float]):
|
||||||
|
lat1 = math.radians(a[0])
|
||||||
|
lon1 = math.radians(a[1])
|
||||||
|
lat2 = math.radians(b[0])
|
||||||
|
lon2 = math.radians(b[1])
|
||||||
|
|
||||||
|
dlon = lon2 - lon1
|
||||||
|
dlat = lat2 - lat1
|
||||||
|
|
||||||
|
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
|
||||||
|
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
||||||
|
|
||||||
|
return EARTH_RADIUS * c
|
||||||
Vendored
+11
-4
@@ -80,10 +80,12 @@ class GPXParser:
|
|||||||
|
|
||||||
@cached()
|
@cached()
|
||||||
async def get_coordinates(self) -> List[Dict[str, float]]:
|
async def get_coordinates(self) -> List[Dict[str, float]]:
|
||||||
|
return await self.average_coordinates(await self.get_coordinates_all())
|
||||||
|
|
||||||
|
@cached()
|
||||||
|
async def get_coordinates_all(self) -> List[Dict[str, float]]:
|
||||||
nodes = self.run_xpath("//gpx:trkpt")
|
nodes = self.run_xpath("//gpx:trkpt")
|
||||||
return await self.average_coordinates(
|
return [{"lat": float(node.attrib["lat"]), "lng": float(node.attrib['lon'])} for node in nodes]
|
||||||
[{"lat": float(node.attrib["lat"]), "lng": float(node.attrib['lon'])} for node in nodes]
|
|
||||||
)
|
|
||||||
|
|
||||||
@cached()
|
@cached()
|
||||||
async def get_speed(self) -> List[float]:
|
async def get_speed(self) -> List[float]:
|
||||||
@@ -141,7 +143,12 @@ class GPXParser:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
extensions = target_node.find("./extensions", self.gpx.getroot().nsmap)
|
extensions = target_node.find("./extensions", self.gpx.getroot().nsmap)
|
||||||
extensions.append(etree.XML(f"<terrain_elevation>{elevation}</terrain_elevation>"))
|
if extensions is not None:
|
||||||
|
extensions.append(etree.XML(f"<terrain_elevation>{elevation}</terrain_elevation>"))
|
||||||
|
else:
|
||||||
|
target_node.append(
|
||||||
|
etree.XML(f"<extensions><terrain_elevation>{elevation}</terrain_elevation></extensions>")
|
||||||
|
)
|
||||||
|
|
||||||
return self.gpx
|
return self.gpx
|
||||||
|
|
||||||
|
|||||||
+19
-52
@@ -8,12 +8,13 @@ import aiohttp
|
|||||||
from sqlalchemy import delete
|
from sqlalchemy import delete
|
||||||
from database import models
|
from database import models
|
||||||
from database.transaction import get_session
|
from database.transaction import get_session
|
||||||
|
from external.gps_utils import get_distance, get_bearing, EARTH_RADIUS
|
||||||
|
|
||||||
|
|
||||||
# https://pyopenair.readthedocs.io/en/latest/openair.html
|
# https://pyopenair.readthedocs.io/en/latest/openair.html
|
||||||
# http://www.winpilot.com/usersguide/userairspace.asp
|
# http://www.winpilot.com/usersguide/userairspace.asp
|
||||||
|
|
||||||
class AirspaceItemType(enum.Enum):
|
class AirspaceType(enum.Enum):
|
||||||
AIRSPACE_A = "A"
|
AIRSPACE_A = "A"
|
||||||
AIRSPACE_B = "B"
|
AIRSPACE_B = "B"
|
||||||
AIRSPACE_C = "C"
|
AIRSPACE_C = "C"
|
||||||
@@ -46,8 +47,8 @@ class Coordinates:
|
|||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class AirspaceItem:
|
class Airspace:
|
||||||
type: AirspaceItemType
|
type: AirspaceType
|
||||||
info: str = ""
|
info: str = ""
|
||||||
upper_limit: str = None
|
upper_limit: str = None
|
||||||
lower_limit: str = None
|
lower_limit: str = None
|
||||||
@@ -57,40 +58,6 @@ class AirspaceItem:
|
|||||||
|
|
||||||
|
|
||||||
class ArcExpander:
|
class ArcExpander:
|
||||||
R = 6378.1 # Radius of the Earth
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_bearing(center_point, end_point):
|
|
||||||
lat3 = math.radians(end_point[0])
|
|
||||||
long3 = math.radians(end_point[1])
|
|
||||||
lat1 = math.radians(center_point[0])
|
|
||||||
long1 = math.radians(center_point[1])
|
|
||||||
|
|
||||||
dLon = long3 - long1
|
|
||||||
|
|
||||||
X = math.cos(lat3) * math.sin(dLon)
|
|
||||||
Y = math.cos(lat1) * math.sin(lat3) - math.sin(lat1) * math.cos(lat3) * math.cos(dLon)
|
|
||||||
|
|
||||||
end_brng = math.atan2(X, Y)
|
|
||||||
|
|
||||||
return end_brng
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_distance(cls, a, b):
|
|
||||||
|
|
||||||
lat1 = math.radians(a[0])
|
|
||||||
lon1 = math.radians(a[1])
|
|
||||||
lat2 = math.radians(b[0])
|
|
||||||
lon2 = math.radians(b[1])
|
|
||||||
|
|
||||||
dlon = lon2 - lon1
|
|
||||||
dlat = lat2 - lat1
|
|
||||||
|
|
||||||
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
|
|
||||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
|
||||||
|
|
||||||
return cls.R * c
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_arc_coordinates(
|
def get_arc_coordinates(
|
||||||
cls,
|
cls,
|
||||||
@@ -98,9 +65,9 @@ class ArcExpander:
|
|||||||
origin_point: tuple[float, float],
|
origin_point: tuple[float, float],
|
||||||
end_point: tuple[float, float]
|
end_point: tuple[float, float]
|
||||||
) -> list[Coordinates]:
|
) -> list[Coordinates]:
|
||||||
initial_bearing = cls.get_bearing(center_point, origin_point)
|
initial_bearing = get_bearing(center_point, origin_point)
|
||||||
target_bearing = cls.get_bearing(center_point, end_point)
|
target_bearing = get_bearing(center_point, end_point)
|
||||||
distance = cls.get_distance(center_point, origin_point)
|
distance = get_distance(center_point, origin_point)
|
||||||
|
|
||||||
print(f"DISTANCE {distance} meters, {center_point=}, {origin_point=}, {end_point=}")
|
print(f"DISTANCE {distance} meters, {center_point=}, {origin_point=}, {end_point=}")
|
||||||
|
|
||||||
@@ -122,12 +89,12 @@ class ArcExpander:
|
|||||||
for bearing in list_bearings:
|
for bearing in list_bearings:
|
||||||
lat1, lon1 = math.radians(center_point[0]), math.radians(center_point[1])
|
lat1, lon1 = math.radians(center_point[0]), math.radians(center_point[1])
|
||||||
lat2 = math.asin(
|
lat2 = math.asin(
|
||||||
math.sin(lat1) * math.cos(distance / cls.R) + math.cos(lat1) * math.sin(distance / cls.R) * math.cos(bearing)
|
math.sin(lat1) * math.cos(distance / EARTH_RADIUS) + math.cos(lat1) * math.sin(distance / EARTH_RADIUS) * math.cos(bearing)
|
||||||
)
|
)
|
||||||
|
|
||||||
lon2 = lon1 + math.atan2(
|
lon2 = lon1 + math.atan2(
|
||||||
math.sin(bearing) * math.sin(distance / cls.R) * math.cos(lat1),
|
math.sin(bearing) * math.sin(distance / EARTH_RADIUS) * math.cos(lat1),
|
||||||
math.cos(distance / cls.R) - math.sin(lat1) * math.sin(lat2)
|
math.cos(distance / EARTH_RADIUS) - math.sin(lat1) * math.sin(lat2)
|
||||||
)
|
)
|
||||||
|
|
||||||
lat2, lon2 = math.degrees(lat2), math.degrees(lon2)
|
lat2, lon2 = math.degrees(lat2), math.degrees(lon2)
|
||||||
@@ -140,7 +107,7 @@ class OpenAirParser:
|
|||||||
def __init__(self, file: Path | str):
|
def __init__(self, file: Path | str):
|
||||||
self.file = None
|
self.file = None
|
||||||
|
|
||||||
self.context_type: Optional[AirspaceItemType] = None
|
self.context_type: Optional[AirspaceType] = None
|
||||||
self.context = {}
|
self.context = {}
|
||||||
|
|
||||||
self.file = file
|
self.file = file
|
||||||
@@ -149,7 +116,7 @@ class OpenAirParser:
|
|||||||
if not self.context_type:
|
if not self.context_type:
|
||||||
return
|
return
|
||||||
|
|
||||||
data = AirspaceItem(type=self.context_type, **self.context)
|
data = Airspace(type=self.context_type, **self.context)
|
||||||
|
|
||||||
self.context_type = None
|
self.context_type = None
|
||||||
self.context = {}
|
self.context = {}
|
||||||
@@ -227,19 +194,19 @@ class OpenAirParser:
|
|||||||
pass
|
pass
|
||||||
case 'AC':
|
case 'AC':
|
||||||
try:
|
try:
|
||||||
self.context_type = AirspaceItemType(value)
|
self.context_type = AirspaceType(value)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"Neznam {e}, ignoruji")
|
print(f"Neznam {e}, ignoruji")
|
||||||
return
|
return
|
||||||
case 'AN':
|
case 'AN':
|
||||||
if self.context_type == AirspaceItemType.AIRSPACE_G:
|
if self.context_type == AirspaceType.AIRSPACE_G:
|
||||||
match value[0:2]:
|
match value[0:2]:
|
||||||
case 'PG':
|
case 'PG':
|
||||||
self.context_type = AirspaceItemType.PARAGLIDING_ZONE
|
self.context_type = AirspaceType.PARAGLIDING_ZONE
|
||||||
case 'LK':
|
case 'LK':
|
||||||
self.context_type = AirspaceItemType.ATZ
|
self.context_type = AirspaceType.ATZ
|
||||||
case _:
|
case _:
|
||||||
self.context_type = AirspaceItemType.TRAGA
|
self.context_type = AirspaceType.TRAGA
|
||||||
|
|
||||||
self.context['info'] = value
|
self.context['info'] = value
|
||||||
case 'AL':
|
case 'AL':
|
||||||
@@ -282,7 +249,7 @@ async def download_airspace(source, destination):
|
|||||||
async def save_airspace(destination):
|
async def save_airspace(destination):
|
||||||
parser = OpenAirParser(destination)
|
parser = OpenAirParser(destination)
|
||||||
async with get_session() as db:
|
async with get_session() as db:
|
||||||
await db.execute(delete(models.AirspaceItem))
|
await db.execute(delete(models.Airspace))
|
||||||
|
|
||||||
for item in parser.parse():
|
for item in parser.parse():
|
||||||
# if 'CTR KARLOVY VARY' not in item.info:
|
# if 'CTR KARLOVY VARY' not in item.info:
|
||||||
@@ -294,7 +261,7 @@ async def save_airspace(destination):
|
|||||||
# print("---" * 10)
|
# print("---" * 10)
|
||||||
|
|
||||||
async with get_session() as db:
|
async with get_session() as db:
|
||||||
await models.AirspaceItem.create(db, {
|
await models.Airspace.create(db, {
|
||||||
"country": "cz",
|
"country": "cz",
|
||||||
"name": item.info,
|
"name": item.info,
|
||||||
"type": item.type.value,
|
"type": item.type.value,
|
||||||
@@ -20,9 +20,33 @@ flight_copilots_dataloader = DataLoader(
|
|||||||
models.flight_has_copilot.c.flight_id == models.Flight.id,
|
models.flight_has_copilot.c.flight_id == models.Flight.id,
|
||||||
models.flight_has_copilot.c.copilot_id == models.Copilot.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)
|
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,
|
||||||
|
relationship_column=models.airport_has_reporting_point.c.airport_id,
|
||||||
|
extra_join=[models.airport_has_reporting_point]
|
||||||
|
).load, cache=False
|
||||||
|
)
|
||||||
|
|
||||||
flights_by_copilot_dataloader = DataLoader(
|
flights_by_copilot_dataloader = DataLoader(
|
||||||
load_fn=MultiModelsDataloader(
|
load_fn=MultiModelsDataloader(
|
||||||
models.Flight,
|
models.Flight,
|
||||||
@@ -150,3 +174,12 @@ photos_aircraft_dataloader = DataLoader(
|
|||||||
).load,
|
).load,
|
||||||
cache=False
|
cache=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
flight_plan_markers_dataloader = DataLoader(
|
||||||
|
load_fn=MultiModelsDataloader(
|
||||||
|
models.FlightPlanMarker,
|
||||||
|
relationship_column=models.FlightPlanMarker.flight_plan_id,
|
||||||
|
order_by=[models.FlightPlanMarker.position, models.FlightPlanMarker.id]
|
||||||
|
).load,
|
||||||
|
cache=False
|
||||||
|
)
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ class AirspaceQueries:
|
|||||||
|
|
||||||
filters = []
|
filters = []
|
||||||
if country:
|
if country:
|
||||||
filters.append(models.AirspaceItem.country == country)
|
filters.append(models.Airspace.country == country)
|
||||||
if types:
|
if types:
|
||||||
filters.append(models.AirspaceItem.type.in_(types))
|
filters.append(models.Airspace.type.in_(types))
|
||||||
|
|
||||||
return await BaseQueryResolver(Airspace, models.AirspaceItem).get_list(
|
return await BaseQueryResolver(Airspace, models.Airspace).get_list(
|
||||||
info.context.user_id,
|
info.context.user_id,
|
||||||
country=country,
|
country=country,
|
||||||
filters=filters
|
filters=filters
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from typing import List
|
||||||
|
import strawberry
|
||||||
|
from strawberry.types import Info
|
||||||
|
from database import models
|
||||||
|
from decorators.error_logging import error_logging
|
||||||
|
from decorators.endpoints import authenticated_user_only, allow_public
|
||||||
|
from graphql_schema.entities.resolvers.base import BaseQueryResolver
|
||||||
|
from graphql_schema.entities.resolvers.flight_plan import FlightPlanMutationResolver
|
||||||
|
from graphql_schema.entities.types.mutation_input import CreateFlightPlanInput, EditFlightPlanInput
|
||||||
|
from graphql_schema.entities.types.types import FlightPlan
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry.type
|
||||||
|
class FlightPlanQueries:
|
||||||
|
@strawberry.field()
|
||||||
|
@error_logging
|
||||||
|
@authenticated_user_only()
|
||||||
|
async def flight_plans(root, info: Info) -> List[FlightPlan]:
|
||||||
|
return await BaseQueryResolver(FlightPlan, models.FlightPlan).get_list(info.context.user_id)
|
||||||
|
|
||||||
|
@strawberry.field()
|
||||||
|
@error_logging
|
||||||
|
@allow_public
|
||||||
|
async def flight_plan(root, info: Info, id: int) -> FlightPlan:
|
||||||
|
return await BaseQueryResolver(FlightPlan, models.FlightPlan).get_one(
|
||||||
|
user_id=info.context.user_id,
|
||||||
|
object_id=id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry.type
|
||||||
|
class FlightPlanMutation:
|
||||||
|
@strawberry.mutation
|
||||||
|
@error_logging
|
||||||
|
@authenticated_user_only()
|
||||||
|
async def create_flight_plan(root, info, input: CreateFlightPlanInput) -> FlightPlan:
|
||||||
|
return await FlightPlanMutationResolver().create(info.context, data=input)
|
||||||
|
|
||||||
|
@strawberry.mutation
|
||||||
|
@error_logging
|
||||||
|
@authenticated_user_only()
|
||||||
|
async def edit_flight_plan(root, info, id: int, input: EditFlightPlanInput) -> FlightPlan:
|
||||||
|
return await FlightPlanMutationResolver().update(id, input, info.context.user_id)
|
||||||
@@ -87,7 +87,7 @@ class BaseQueryResolver(BaseResolver):
|
|||||||
|
|
||||||
|
|
||||||
class BaseMutationResolver(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)
|
query = self.query_builder.get_simple_query(created_by_id=created_by_id).filter(self.model.id == id)
|
||||||
return (await db.scalars(query)).one()
|
return (await db.scalars(query)).one()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
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):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(graphql_type=FlightPlan, model=models.FlightPlan)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def save_markers(db, flight_plan: models.FlightPlan, markers: list):
|
||||||
|
position = 0
|
||||||
|
|
||||||
|
for marker in markers:
|
||||||
|
await models.FlightPlanMarker.create(db, data={
|
||||||
|
"position": position,
|
||||||
|
"flight_plan_id": flight_plan.id,
|
||||||
|
"type": marker.type,
|
||||||
|
"name": marker.name,
|
||||||
|
"gps_latitude": marker.gps_latitude,
|
||||||
|
"gps_longitude": marker.gps_longitude
|
||||||
|
})
|
||||||
|
position += 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def reset_plan_markers(db, flight_plan: models.FlightPlan):
|
||||||
|
await db.execute(
|
||||||
|
delete(models.FlightPlanMarker)
|
||||||
|
.filter(models.FlightPlanMarker.flight_plan_id == flight_plan.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def create(self, context, data: CreateFlightPlanInput) -> FlightPlan:
|
||||||
|
input_data = data.to_dict()
|
||||||
|
input_data['created_by_id'] = context.user_id
|
||||||
|
|
||||||
|
async with get_session() as db:
|
||||||
|
flight_plan = await self._do_create(db, data=input_data)
|
||||||
|
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_model = await self._get_one(db, id=id, created_by_id=user_id)
|
||||||
|
|
||||||
|
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 strawberry.file_uploads import Upload
|
||||||
from database import models
|
from database import models
|
||||||
from graphql_schema.entities.types.base import BaseGraphqlInputType
|
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
|
from graphql_schema.sqlalchemy_to_strawberry_type import strawberry_sqlalchemy_input
|
||||||
|
|
||||||
|
|
||||||
@@ -14,11 +15,33 @@ class ComboboxInput:
|
|||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_sqlalchemy_input(model=models.FlightPlanMarker, exclude_fields=["id", "flight_plan_id"])
|
||||||
|
class FlightPlanMarkerInput(BaseGraphqlInputType):
|
||||||
|
position: Optional[int] = 0
|
||||||
|
|
||||||
|
|
||||||
@strawberry_sqlalchemy_input(model=models.Copilot, exclude_fields=["id"])
|
@strawberry_sqlalchemy_input(model=models.Copilot, exclude_fields=["id"])
|
||||||
class CreateCopilotInput(BaseGraphqlInputType):
|
class CreateCopilotInput(BaseGraphqlInputType):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_sqlalchemy_input(model=models.FlightPlan, exclude_fields=["id", "markers"])
|
||||||
|
class CreateFlightPlanInput(BaseGraphqlInputType):
|
||||||
|
markers: list[FlightPlanMarkerInput]
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
data = super().to_dict()
|
||||||
|
data['markers'] = [marker.to_dict() for marker in self.markers]
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_sqlalchemy_input(model=models.FlightPlan, exclude_fields=["id", "markers"], all_optional=True)
|
||||||
|
class EditFlightPlanInput(BaseGraphqlInputType):
|
||||||
|
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)
|
@strawberry_sqlalchemy_input(model=models.Copilot, exclude_fields=["id"], all_optional=True)
|
||||||
class EditCopilotInput(BaseGraphqlInputType):
|
class EditCopilotInput(BaseGraphqlInputType):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
import strawberry
|
import strawberry
|
||||||
from database import models
|
from database import models
|
||||||
from decorators.endpoints import authenticated_user_only
|
from decorators.endpoints import authenticated_user_only
|
||||||
|
from external.gps_utils import get_bearing, get_distance
|
||||||
from external.gpx_parser import GPXParser
|
from external.gpx_parser import GPXParser
|
||||||
from graphql_schema.dataloaders.flight_duration import flight_duration_dataloader
|
from graphql_schema.dataloaders.flight_duration import flight_duration_dataloader
|
||||||
from graphql_schema.dataloaders.multi_models import (
|
from graphql_schema.dataloaders.multi_models import (
|
||||||
@@ -11,7 +14,8 @@ from graphql_schema.dataloaders.multi_models import (
|
|||||||
photos_dataloader, flights_by_aircraft_dataloader, users_in_organization_dataloader,
|
photos_dataloader, flights_by_aircraft_dataloader, users_in_organization_dataloader,
|
||||||
aircrafts_from_organization_dataloader, user_organizations_dataloader, flights_by_event_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
|
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 (
|
from graphql_schema.dataloaders.single_model import (
|
||||||
poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader,
|
poi_dataloader, poi_type_dataloader, event_dataloader, aircraft_dataloader, airport_dataloader,
|
||||||
@@ -43,9 +47,19 @@ class GPXTrack:
|
|||||||
avg_altitude: float
|
avg_altitude: float
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_sqlalchemy_type(models.ReportingPoint)
|
||||||
|
class ReportingPoint:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@strawberry_sqlalchemy_type(models.Airport)
|
@strawberry_sqlalchemy_type(models.Airport)
|
||||||
class Airport:
|
class Airport:
|
||||||
pass
|
reporting_points: List[ReportingPoint] = strawberry.field(
|
||||||
|
resolver=lambda root: reporting_points_dataloader.load(root.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: loader na ikonu
|
||||||
|
# TODO: loader na RWY a frekvence
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
@@ -55,15 +69,12 @@ class Airspace:
|
|||||||
country: str
|
country: str
|
||||||
type: str
|
type: str
|
||||||
lower_limit: str
|
lower_limit: str
|
||||||
|
lower_alt_unit: str
|
||||||
|
upper_alt_unit: str
|
||||||
upper_limit: str
|
upper_limit: str
|
||||||
center: Optional[tuple[float, float]]
|
|
||||||
radius: Optional[float]
|
|
||||||
bounds: list[tuple[float, float]]
|
bounds: list[tuple[float, float]]
|
||||||
map_center: Optional[Point] = strawberry.field(
|
|
||||||
resolver=lambda root: Point(lat=root.center[0], lng=root.center[1]) if root.center else None
|
|
||||||
)
|
|
||||||
map_bounds: list[Point] = strawberry.field(
|
map_bounds: list[Point] = strawberry.field(
|
||||||
resolver=lambda root: [Point(lat=point[0], lng=point[1]) for point in root.bounds]
|
resolver=lambda root: [Point(lat=point[1], lng=point[0]) for point in root.bounds]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -180,6 +191,57 @@ class Flight:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_sqlalchemy_type(model=models.FlightPlanMarker)
|
||||||
|
class FlightPlanMarker:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry.type
|
||||||
|
class FlightPlanTrack:
|
||||||
|
bearing: Optional[int]
|
||||||
|
distance: Optional[float]
|
||||||
|
from_: FlightPlanMarker = strawberry.field(name="from")
|
||||||
|
to: Optional[FlightPlanMarker]
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry_sqlalchemy_type(model=models.FlightPlan)
|
||||||
|
class FlightPlan:
|
||||||
|
async def resolve_track(self):
|
||||||
|
markers = await self.markers()
|
||||||
|
|
||||||
|
if len(markers) == 1:
|
||||||
|
return [FlightPlanTrack(bearing=None, distance=None, from_=markers[0], to=None)]
|
||||||
|
|
||||||
|
navigation = []
|
||||||
|
for i in range(len(markers) - 1):
|
||||||
|
from_ = markers[i]
|
||||||
|
to = markers[i + 1]
|
||||||
|
|
||||||
|
point_from = (from_.gps_latitude, from_.gps_longitude)
|
||||||
|
point_to = (to.gps_latitude, to.gps_longitude)
|
||||||
|
|
||||||
|
navigation.append(FlightPlanTrack(
|
||||||
|
bearing=round(math.degrees(get_bearing(point_from, point_to)) % 360),
|
||||||
|
distance=round(get_distance(point_from, point_to), 1),
|
||||||
|
from_=from_,
|
||||||
|
to=to)
|
||||||
|
)
|
||||||
|
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)
|
@strawberry_sqlalchemy_type(models.Copilot)
|
||||||
class Copilot:
|
class Copilot:
|
||||||
async def resolve_flights(root, info):
|
async def resolve_flights(root, info):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from graphql_schema.entities.aircraft import AircraftMutation
|
|||||||
from graphql_schema.entities.copilot import CopilotMutation
|
from graphql_schema.entities.copilot import CopilotMutation
|
||||||
from graphql_schema.entities.event import EventMutation
|
from graphql_schema.entities.event import EventMutation
|
||||||
from graphql_schema.entities.flight import FlightMutation
|
from graphql_schema.entities.flight import FlightMutation
|
||||||
|
from graphql_schema.entities.flight_plan import FlightPlanMutation
|
||||||
from graphql_schema.entities.organization import OrganizationUserMutation, OrganizationMutation
|
from graphql_schema.entities.organization import OrganizationUserMutation, OrganizationMutation
|
||||||
from graphql_schema.entities.photo import PhotoMutation
|
from graphql_schema.entities.photo import PhotoMutation
|
||||||
from graphql_schema.entities.poi import PointOfInterestMutation
|
from graphql_schema.entities.poi import PointOfInterestMutation
|
||||||
@@ -11,6 +12,7 @@ from graphql_schema.entities.user import EditUserMutation
|
|||||||
Mutation = merge_types("Mutation", (
|
Mutation = merge_types("Mutation", (
|
||||||
AircraftMutation,
|
AircraftMutation,
|
||||||
FlightMutation,
|
FlightMutation,
|
||||||
|
FlightPlanMutation,
|
||||||
PhotoMutation,
|
PhotoMutation,
|
||||||
PointOfInterestMutation,
|
PointOfInterestMutation,
|
||||||
CopilotMutation,
|
CopilotMutation,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from .entities.airspace import AirspaceQueries
|
|||||||
from .entities.copilot import CopilotQueries
|
from .entities.copilot import CopilotQueries
|
||||||
from .entities.event import EventQueries
|
from .entities.event import EventQueries
|
||||||
from .entities.flight import FlightQueries
|
from .entities.flight import FlightQueries
|
||||||
|
from .entities.flight_plan import FlightPlanQueries
|
||||||
from .entities.organization import OrganizationQueries
|
from .entities.organization import OrganizationQueries
|
||||||
from .entities.photo import PhotoQueries
|
from .entities.photo import PhotoQueries
|
||||||
from .entities.poi import PointOfInterestQueries
|
from .entities.poi import PointOfInterestQueries
|
||||||
@@ -19,6 +20,7 @@ Query = merge_types('Query', (
|
|||||||
AirportQueries,
|
AirportQueries,
|
||||||
AirspaceQueries,
|
AirspaceQueries,
|
||||||
FlightQueries,
|
FlightQueries,
|
||||||
|
FlightPlanQueries,
|
||||||
CopilotQueries,
|
CopilotQueries,
|
||||||
UserQueries,
|
UserQueries,
|
||||||
PhotoQueries,
|
PhotoQueries,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
import sys
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
sys.path.insert(0, "/app/src")
|
sys.path.insert(0, "/app/src")
|
||||||
from paths import FLIGHT_GPX_TRACK_PATH # noqa
|
from paths import FLIGHT_GPX_TRACK_PATH # noqa
|
||||||
from database import async_session, models # noqa
|
from database import async_session, models # noqa
|
||||||
@@ -46,13 +45,18 @@ async def add_elevation_to_tracks():
|
|||||||
|
|
||||||
for flight in flights:
|
for flight in flights:
|
||||||
gpx_file = f"{FLIGHT_GPX_TRACK_PATH}/{flight.gpx_track_filename}"
|
gpx_file = f"{FLIGHT_GPX_TRACK_PATH}/{flight.gpx_track_filename}"
|
||||||
gpx = GPXParser(gpx_file)
|
try:
|
||||||
|
gpx = GPXParser(gpx_file)
|
||||||
|
except OSError:
|
||||||
|
print(f"ERROR, cannot open {gpx_file=}, skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
coordinates = await gpx.get_coordinates()
|
coordinates = await gpx.get_coordinates_all()
|
||||||
elevation = await elevation_api.get_elevation_for_points(coordinates)
|
elevation = await elevation_api.get_elevation_for_points(coordinates)
|
||||||
|
|
||||||
gpx_with_elevation = gpx.add_terrain_elevation(elevation)
|
gpx_with_elevation = gpx.add_terrain_elevation(elevation)
|
||||||
|
|
||||||
output_name = f"terrain_{flight.gpx_track_filename[30:]}"
|
output_name = f"terrain_{flight.gpx_track_filename[-30:]}"
|
||||||
gpx.write(gpx_with_elevation, output=f"{FLIGHT_GPX_TRACK_PATH}/{output_name}")
|
gpx.write(gpx_with_elevation, output=f"{FLIGHT_GPX_TRACK_PATH}/{output_name}")
|
||||||
await models.Flight.update(
|
await models.Flight.update(
|
||||||
db_session=session, obj=flight, data={
|
db_session=session, obj=flight, data={
|
||||||
|
|||||||
Reference in New Issue
Block a user