stub out v1 api
This commit is contained in:
parent
f56de15674
commit
2dbb0a8416
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
.ruff_cache/
|
.ruff_cache/
|
||||||
|
notes.txt
|
||||||
|
|
||||||
|
138
alembic/versions/52bdefdbf755_initial_db.py
Normal file
138
alembic/versions/52bdefdbf755_initial_db.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
"""initial db
|
||||||
|
|
||||||
|
Revision ID: 52bdefdbf755
|
||||||
|
Revises:
|
||||||
|
Create Date: 2024-05-27 13:59:24.644977
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "52bdefdbf755"
|
||||||
|
down_revision: Union[str, None] = None
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table(
|
||||||
|
"tenants",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("name", sa.String(), nullable=False),
|
||||||
|
sa.Column("hostname", sa.String(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"aircraft",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("tenant_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("icao", sa.String(), nullable=False),
|
||||||
|
sa.Column("tail_number", sa.String(), nullable=True),
|
||||||
|
sa.Column("range_nm", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["tenant_id"],
|
||||||
|
["tenants.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
sa.UniqueConstraint("icao", "tail_number"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"airlines",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("tenant_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("name", sa.String(), nullable=False),
|
||||||
|
sa.Column("iata", sa.String(), nullable=False),
|
||||||
|
sa.Column("icao", sa.String(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["tenant_id"],
|
||||||
|
["tenants.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"airports",
|
||||||
|
sa.Column("icao", sa.String(), nullable=False),
|
||||||
|
sa.Column("tenant_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("iata", sa.String(), nullable=False),
|
||||||
|
sa.Column("name", sa.String(), nullable=False),
|
||||||
|
sa.Column("city", sa.String(), nullable=False),
|
||||||
|
sa.Column("country", sa.String(), nullable=False),
|
||||||
|
sa.Column("latitude", sa.Float(), nullable=False),
|
||||||
|
sa.Column("longitude", sa.Float(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["tenant_id"],
|
||||||
|
["tenants.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("icao"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"users",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("tenant_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("name", sa.String(), nullable=False),
|
||||||
|
sa.Column("email", sa.String(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["tenant_id"],
|
||||||
|
["tenants.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"flights",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("tenant_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("departure_icao", sa.String(), nullable=False),
|
||||||
|
sa.Column("arrival_icao", sa.String(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.Column("aircraft_id", sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["aircraft_id"],
|
||||||
|
["aircraft.id"],
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["tenant_id"],
|
||||||
|
["tenants.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"livery",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("tenant_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("name", sa.String(), nullable=False),
|
||||||
|
sa.Column("aircraft_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["aircraft_id"],
|
||||||
|
["aircraft.id"],
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["tenant_id"],
|
||||||
|
["tenants.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table("livery")
|
||||||
|
op.drop_table("flights")
|
||||||
|
op.drop_table("users")
|
||||||
|
op.drop_table("airports")
|
||||||
|
op.drop_table("airlines")
|
||||||
|
op.drop_table("aircraft")
|
||||||
|
op.drop_table("tenants")
|
||||||
|
# ### end Alembic commands ###
|
@ -1,37 +0,0 @@
|
|||||||
"""add user
|
|
||||||
|
|
||||||
Revision ID: d487ed348c4c
|
|
||||||
Revises:
|
|
||||||
Create Date: 2024-05-23 22:06:52.412454
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = "d487ed348c4c"
|
|
||||||
down_revision: Union[str, None] = None
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.create_table(
|
|
||||||
"users",
|
|
||||||
sa.Column("id", sa.Integer(), nullable=False),
|
|
||||||
sa.Column("name", sa.String(), nullable=False),
|
|
||||||
sa.Column("email", sa.String(), nullable=False),
|
|
||||||
sa.PrimaryKeyConstraint("id"),
|
|
||||||
)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_table("users")
|
|
||||||
# ### end Alembic commands ###
|
|
179
poetry.lock
generated
179
poetry.lock
generated
@ -19,6 +19,31 @@ typing-extensions = ">=4"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
tz = ["backports.zoneinfo"]
|
tz = ["backports.zoneinfo"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aniso8601"
|
||||||
|
version = "9.0.1"
|
||||||
|
description = "A library for parsing ISO 8601 strings."
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
|
||||||
|
{file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "annotated-types"
|
||||||
|
version = "0.7.0"
|
||||||
|
description = "Reusable constraint types to use with typing.Annotated"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
|
||||||
|
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blinker"
|
name = "blinker"
|
||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
@ -141,6 +166,26 @@ Werkzeug = ">=3.0.0"
|
|||||||
async = ["asgiref (>=3.2)"]
|
async = ["asgiref (>=3.2)"]
|
||||||
dotenv = ["python-dotenv"]
|
dotenv = ["python-dotenv"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flask-restful"
|
||||||
|
version = "0.3.10"
|
||||||
|
description = "Simple framework for creating REST APIs"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "Flask-RESTful-0.3.10.tar.gz", hash = "sha256:fe4af2ef0027df8f9b4f797aba20c5566801b6ade995ac63b588abf1a59cec37"},
|
||||||
|
{file = "Flask_RESTful-0.3.10-py2.py3-none-any.whl", hash = "sha256:1cf93c535172f112e080b0d4503a8d15f93a48c88bdd36dd87269bdaf405051b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
aniso8601 = ">=0.82"
|
||||||
|
Flask = ">=0.8"
|
||||||
|
pytz = "*"
|
||||||
|
six = ">=1.3.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["sphinx"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flask-sqlalchemy"
|
name = "flask-sqlalchemy"
|
||||||
version = "3.1.1"
|
version = "3.1.1"
|
||||||
@ -424,6 +469,116 @@ docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)"
|
|||||||
pool = ["psycopg-pool"]
|
pool = ["psycopg-pool"]
|
||||||
test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
|
test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pydantic"
|
||||||
|
version = "2.7.1"
|
||||||
|
description = "Data validation using Python type hints"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
|
||||||
|
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
annotated-types = ">=0.4.0"
|
||||||
|
pydantic-core = "2.18.2"
|
||||||
|
typing-extensions = ">=4.6.1"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
email = ["email-validator (>=2.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pydantic-core"
|
||||||
|
version = "2.18.2"
|
||||||
|
description = "Core functionality for Pydantic validation and serialization"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
|
||||||
|
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
|
||||||
|
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
|
||||||
|
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydeps"
|
name = "pydeps"
|
||||||
version = "1.12.20"
|
version = "1.12.20"
|
||||||
@ -476,6 +631,28 @@ pytest = ">=4.6"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
|
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytz"
|
||||||
|
version = "2024.1"
|
||||||
|
description = "World timezone definitions, modern and historical"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
|
||||||
|
{file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.16.0"
|
||||||
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
files = [
|
||||||
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlalchemy"
|
name = "sqlalchemy"
|
||||||
version = "2.0.30"
|
version = "2.0.30"
|
||||||
@ -623,4 +800,4 @@ watchdog = ["watchdog (>=2.3)"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "21c13832e26aeda993299db3cf7414cac6f000f61f905516d8399026943bfadb"
|
content-hash = "e63de32ed89a7884b852f1a883d4e7cfe58ec7d010d95a230d436ec82dacf4b4"
|
||||||
|
@ -17,6 +17,8 @@ flask = "^3.0.3"
|
|||||||
flask-sqlalchemy = "^3.1.1"
|
flask-sqlalchemy = "^3.1.1"
|
||||||
psycopg = "^3.1.19"
|
psycopg = "^3.1.19"
|
||||||
alembic = "^1.13.1"
|
alembic = "^1.13.1"
|
||||||
|
pydantic = "^2.7.1"
|
||||||
|
flask-restful = "^0.3.10"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
teufa = "teufa.cli:cli"
|
teufa = "teufa.cli:cli"
|
||||||
|
@ -1,9 +1,31 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
from flask import Flask, g
|
||||||
|
from flask.testing import FlaskClient
|
||||||
|
|
||||||
|
from teufa import db as dbm
|
||||||
from teufa.app import create_app
|
from teufa.app import create_app
|
||||||
|
from teufa.ext import db
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def app():
|
def app():
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
with app.app_context():
|
||||||
|
dbm.Base.metadata.create_all(db.engine)
|
||||||
|
|
||||||
|
tenant = dbm.Tenant(
|
||||||
|
name="Default",
|
||||||
|
hostname="localhost",
|
||||||
|
)
|
||||||
|
db.session.add(tenant)
|
||||||
|
db.session.commit()
|
||||||
|
g.tenant = tenant
|
||||||
|
|
||||||
yield app
|
yield app
|
||||||
|
|
||||||
|
dbm.Base.metadata.drop_all(db.engine)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client(app: Flask) -> FlaskClient:
|
||||||
|
return app.test_client()
|
||||||
|
173
tests/test_flight.py
Normal file
173
tests/test_flight.py
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from flask import g
|
||||||
|
from flask.testing import FlaskClient
|
||||||
|
from sqlalchemy.sql import func, select
|
||||||
|
|
||||||
|
from teufa import db as dbm
|
||||||
|
from teufa.ext import db
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_flight(client: FlaskClient):
|
||||||
|
aircraft = dbm.Aircraft(
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
icao="B737",
|
||||||
|
tail_number="N12345",
|
||||||
|
range_nm=3000,
|
||||||
|
)
|
||||||
|
db.session.add(aircraft)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
"/api/v1/flights",
|
||||||
|
json={
|
||||||
|
"flight": {
|
||||||
|
"departure_icao": "KDEN",
|
||||||
|
"arrival_icao": "KLGA",
|
||||||
|
"aircraft_id": aircraft.id,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert json.loads(response.json) == {
|
||||||
|
"flight": {
|
||||||
|
"id": 1,
|
||||||
|
"departure_icao": "KDEN",
|
||||||
|
"arrival_icao": "KLGA",
|
||||||
|
"aircraft_id": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_flight(client: FlaskClient):
|
||||||
|
aircraft = dbm.Aircraft(
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
icao="B737",
|
||||||
|
tail_number="N12345",
|
||||||
|
range_nm=3000,
|
||||||
|
)
|
||||||
|
db.session.add(aircraft)
|
||||||
|
db.session.commit()
|
||||||
|
db.session.add(
|
||||||
|
dbm.Flight(
|
||||||
|
id=1,
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
departure_icao="KDEN",
|
||||||
|
arrival_icao="KLGA",
|
||||||
|
aircraft_id=aircraft.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
response = client.get("/api/v1/flights/1")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert json.loads(response.json) == {
|
||||||
|
"flight": {
|
||||||
|
"id": 1,
|
||||||
|
"departure_icao": "KDEN",
|
||||||
|
"arrival_icao": "KLGA",
|
||||||
|
"aircraft_id": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_flight_not_found(client: FlaskClient):
|
||||||
|
response = client.get("/api/v1/flights/1")
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert json.loads(response.json) == {"message": "Flight not found"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_flight(client: FlaskClient):
|
||||||
|
aircraft = dbm.Aircraft(
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
icao="B737",
|
||||||
|
tail_number="N12345",
|
||||||
|
range_nm=3000,
|
||||||
|
)
|
||||||
|
db.session.add(aircraft)
|
||||||
|
db.session.commit()
|
||||||
|
db.session.add(
|
||||||
|
dbm.Flight(
|
||||||
|
id=1,
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
departure_icao="KDEN",
|
||||||
|
arrival_icao="KLGA",
|
||||||
|
aircraft_id=aircraft.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
response = client.put(
|
||||||
|
"/api/v1/flights/1",
|
||||||
|
json={
|
||||||
|
"flight": {
|
||||||
|
"departure_icao": "KJFK",
|
||||||
|
"arrival_icao": "KLAX",
|
||||||
|
"aircraft_id": 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert json.loads(response.json) == {
|
||||||
|
"flight": {
|
||||||
|
"id": 1,
|
||||||
|
"departure_icao": "KJFK",
|
||||||
|
"arrival_icao": "KLAX",
|
||||||
|
"aircraft_id": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_flight_not_found(client: FlaskClient):
|
||||||
|
response = client.put(
|
||||||
|
"/api/v1/flights/1",
|
||||||
|
json={
|
||||||
|
"flight": {
|
||||||
|
"departure_icao": "KJFK",
|
||||||
|
"arrival_icao": "KLAX",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert json.loads(response.json) == {"message": "Flight not found"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_flight(client: FlaskClient):
|
||||||
|
aircraft = dbm.Aircraft(
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
icao="B737",
|
||||||
|
tail_number="N12345",
|
||||||
|
range_nm=3000,
|
||||||
|
)
|
||||||
|
db.session.add(aircraft)
|
||||||
|
db.session.commit()
|
||||||
|
db.session.add(
|
||||||
|
dbm.Flight(
|
||||||
|
id=1,
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
departure_icao="KDEN",
|
||||||
|
arrival_icao="KLGA",
|
||||||
|
aircraft_id=aircraft.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
response = client.delete("/api/v1/flights/1")
|
||||||
|
|
||||||
|
assert response.status_code == 204
|
||||||
|
assert response.data == b""
|
||||||
|
|
||||||
|
with db.session.begin():
|
||||||
|
assert db.session.scalar(select(func.count(dbm.Flight.id))) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_flight_not_found(client: FlaskClient):
|
||||||
|
response = client.delete("/api/v1/flights/1")
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert json.loads(response.json) == {"message": "Flight not found"}
|
21
teufa/app.py
21
teufa/app.py
@ -1,7 +1,10 @@
|
|||||||
from flask import Flask
|
from flask import Flask, g, request
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
|
from . import db as dbm
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .ext import db
|
from .ext import db
|
||||||
|
from .v1_api import bp as v1_bp
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
@ -10,4 +13,20 @@ def create_app():
|
|||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def before_request():
|
||||||
|
if not hasattr(g, "tenant"):
|
||||||
|
hostname = request.host.split(":")[0]
|
||||||
|
g.tenant = db.session.scalars(
|
||||||
|
select(dbm.Tenant).filter_by(hostname=hostname).limit(1)
|
||||||
|
).first()
|
||||||
|
|
||||||
|
@app.teardown_request
|
||||||
|
def teardown_request(exc):
|
||||||
|
if exc:
|
||||||
|
db.session.rollback()
|
||||||
|
db.session.remove()
|
||||||
|
|
||||||
|
app.register_blueprint(v1_bp)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
41
teufa/dao.py
Normal file
41
teufa/dao.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
empty = object()
|
||||||
|
|
||||||
|
|
||||||
|
class Error(BaseModel):
|
||||||
|
message: str
|
||||||
|
|
||||||
|
|
||||||
|
class Flight(BaseModel):
|
||||||
|
id: int | None = None
|
||||||
|
departure_icao: str
|
||||||
|
arrival_icao: str
|
||||||
|
aircraft_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class PartialFlight(BaseModel):
|
||||||
|
id: int | object = empty
|
||||||
|
departure_icao: str | object = empty
|
||||||
|
arrival_icao: str | object = empty
|
||||||
|
aircraft_id: int | object = empty
|
||||||
|
|
||||||
|
|
||||||
|
class CreateFlightRequest(BaseModel):
|
||||||
|
flight: Flight
|
||||||
|
|
||||||
|
|
||||||
|
class CreateFlightResponse(BaseModel):
|
||||||
|
flight: Flight
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateFlightRequest(BaseModel):
|
||||||
|
flight: PartialFlight
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateFlightResponse(BaseModel):
|
||||||
|
flight: Flight
|
||||||
|
|
||||||
|
|
||||||
|
class GetFlightResponse(BaseModel):
|
||||||
|
flight: Flight
|
109
teufa/db.py
109
teufa/db.py
@ -1,13 +1,120 @@
|
|||||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlalchemy import ForeignKey, UniqueConstraint, func
|
||||||
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
class Base(DeclarativeBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Tenant(Base):
|
||||||
|
__tablename__ = "tenants"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
name: Mapped[str]
|
||||||
|
hostname: Mapped[str]
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
|
||||||
|
users: Mapped[list["User"]] = relationship(
|
||||||
|
back_populates="tenant", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
airlines: Mapped[list["Airline"]] = relationship(
|
||||||
|
back_populates="tenant", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
airports: Mapped[list["Airport"]] = relationship(
|
||||||
|
back_populates="tenant", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
fleet: Mapped[list["Aircraft"]] = relationship(
|
||||||
|
back_populates="tenant", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
liveries: Mapped[list["Livery"]] = relationship(
|
||||||
|
back_populates="tenant", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
flights: Mapped[list["Flight"]] = relationship(
|
||||||
|
back_populates="tenant", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
class User(Base):
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
|
||||||
name: Mapped[str]
|
name: Mapped[str]
|
||||||
email: Mapped[str]
|
email: Mapped[str]
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
|
||||||
|
tenant: Mapped["Tenant"] = relationship(back_populates="users")
|
||||||
|
|
||||||
|
|
||||||
|
class Airline(Base):
|
||||||
|
__tablename__ = "airlines"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
|
||||||
|
name: Mapped[str]
|
||||||
|
iata: Mapped[str]
|
||||||
|
icao: Mapped[str]
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
|
||||||
|
tenant: Mapped["Tenant"] = relationship(back_populates="airlines")
|
||||||
|
|
||||||
|
|
||||||
|
class Airport(Base):
|
||||||
|
__tablename__ = "airports"
|
||||||
|
|
||||||
|
icao: Mapped[str] = mapped_column(primary_key=True)
|
||||||
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
|
||||||
|
iata: Mapped[str]
|
||||||
|
name: Mapped[str]
|
||||||
|
city: Mapped[str]
|
||||||
|
country: Mapped[str]
|
||||||
|
latitude: Mapped[float]
|
||||||
|
longitude: Mapped[float]
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
|
||||||
|
tenant: Mapped["Tenant"] = relationship(back_populates="airports")
|
||||||
|
|
||||||
|
|
||||||
|
class Aircraft(Base):
|
||||||
|
__tablename__ = "aircraft"
|
||||||
|
__table_args__ = (UniqueConstraint("icao", "tail_number"),)
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
|
||||||
|
icao: Mapped[str]
|
||||||
|
tail_number: Mapped[str | None]
|
||||||
|
range_nm: Mapped[int]
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
|
||||||
|
tenant: Mapped["Tenant"] = relationship(back_populates="fleet")
|
||||||
|
liveries: Mapped[list["Livery"]] = relationship(back_populates="aircraft")
|
||||||
|
flights: Mapped[list["Flight"]] = relationship(back_populates="aircraft")
|
||||||
|
|
||||||
|
|
||||||
|
class Livery(Base):
|
||||||
|
__tablename__ = "livery"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
|
||||||
|
name: Mapped[str]
|
||||||
|
aircraft_id: Mapped[int] = mapped_column(ForeignKey("aircraft.id"))
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
|
||||||
|
tenant: Mapped["Tenant"] = relationship(back_populates="liveries")
|
||||||
|
aircraft: Mapped["Aircraft"] = relationship(back_populates="liveries")
|
||||||
|
|
||||||
|
|
||||||
|
class Flight(Base):
|
||||||
|
__tablename__ = "flights"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
|
||||||
|
departure_icao: Mapped[str]
|
||||||
|
arrival_icao: Mapped[str]
|
||||||
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
||||||
|
aircraft_id: Mapped["Aircraft"] = mapped_column(ForeignKey("aircraft.id"))
|
||||||
|
|
||||||
|
tenant: Mapped["Tenant"] = relationship(back_populates="flights")
|
||||||
|
aircraft: Mapped["Aircraft"] = relationship(back_populates="flights")
|
||||||
|
10
teufa/v1_api/__init__.py
Normal file
10
teufa/v1_api/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
from flask_restful import Api
|
||||||
|
|
||||||
|
from .flights import FlightCollectionResource, FlightResource
|
||||||
|
|
||||||
|
bp = Blueprint("api", __name__, url_prefix="/api")
|
||||||
|
api = Api(bp)
|
||||||
|
|
||||||
|
api.add_resource(FlightCollectionResource, "/v1/flights")
|
||||||
|
api.add_resource(FlightResource, "/v1/flights/<int:flight_id>")
|
102
teufa/v1_api/flights.py
Normal file
102
teufa/v1_api/flights.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
from flask import g, request
|
||||||
|
from flask_restful import Resource
|
||||||
|
|
||||||
|
from .. import dao
|
||||||
|
from .. import db as dbm
|
||||||
|
from ..ext import db
|
||||||
|
|
||||||
|
|
||||||
|
class FlightCollectionResource(Resource):
|
||||||
|
def post(self):
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
req = dao.CreateFlightRequest(**data)
|
||||||
|
|
||||||
|
flight = dbm.Flight(
|
||||||
|
tenant_id=g.tenant.id,
|
||||||
|
departure_icao=req.flight.departure_icao,
|
||||||
|
arrival_icao=req.flight.arrival_icao,
|
||||||
|
aircraft_id=req.flight.aircraft_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(flight)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
res = dao.CreateFlightResponse(
|
||||||
|
**{
|
||||||
|
"flight": {
|
||||||
|
"id": flight.id,
|
||||||
|
"departure_icao": flight.departure_icao,
|
||||||
|
"arrival_icao": flight.arrival_icao,
|
||||||
|
"aircraft_id": flight.aircraft_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.model_dump_json(), 201
|
||||||
|
|
||||||
|
|
||||||
|
class FlightResource(Resource):
|
||||||
|
def get(self, flight_id):
|
||||||
|
flight = db.session.get(dbm.Flight, flight_id)
|
||||||
|
|
||||||
|
if not flight:
|
||||||
|
return dao.Error(message="Flight not found").model_dump_json(), 404
|
||||||
|
|
||||||
|
res = dao.GetFlightResponse(
|
||||||
|
**{
|
||||||
|
"flight": {
|
||||||
|
"id": flight.id,
|
||||||
|
"departure_icao": flight.departure_icao,
|
||||||
|
"arrival_icao": flight.arrival_icao,
|
||||||
|
"aircraft_id": flight.aircraft_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.model_dump_json()
|
||||||
|
|
||||||
|
def put(self, flight_id):
|
||||||
|
flight = db.session.get(dbm.Flight, flight_id)
|
||||||
|
|
||||||
|
if not flight:
|
||||||
|
return dao.Error(message="Flight not found").model_dump_json(), 404
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
req = dao.UpdateFlightRequest(**data)
|
||||||
|
|
||||||
|
if req.flight.id is not dao.empty:
|
||||||
|
flight.id = req.flight.id
|
||||||
|
if req.flight.departure_icao is not dao.empty:
|
||||||
|
flight.departure_icao = req.flight.departure_icao
|
||||||
|
if req.flight.arrival_icao is not dao.empty:
|
||||||
|
flight.arrival_icao = req.flight.arrival_icao
|
||||||
|
if req.flight.aircraft_id is not dao.empty:
|
||||||
|
flight.aircraft_id = req.flight.aircraft_id
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
res = dao.UpdateFlightResponse(
|
||||||
|
**{
|
||||||
|
"flight": {
|
||||||
|
"id": flight.id,
|
||||||
|
"departure_icao": flight.departure_icao,
|
||||||
|
"arrival_icao": flight.arrival_icao,
|
||||||
|
"aircraft_id": flight.aircraft_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.model_dump_json()
|
||||||
|
|
||||||
|
def delete(self, flight_id):
|
||||||
|
flight = db.session.get(dbm.Flight, flight_id)
|
||||||
|
|
||||||
|
if not flight:
|
||||||
|
return dao.Error(message="Flight not found").model_dump_json(), 404
|
||||||
|
|
||||||
|
db.session.delete(flight)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return "", 204
|
Loading…
x
Reference in New Issue
Block a user