Reformatting everything with black
This commit is contained in:
1 parent
7bd07f084a
commit
8185f1f8b3
48 files changed
+996
-344
No files matched your search
@@ -4,15 +4,15 @@ Revision ID: 0c3d1a5cff1f
|
||||
Revises: 5b6a7817b408
|
||||
Create Date: 2026-07-02 12:22:24.106795
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '0c3d1a5cff1f'
|
||||
down_revision: Union[str, None] = '5b6a7817b408'
|
||||
revision: str = "0c3d1a5cff1f"
|
||||
down_revision: Union[str, None] = "5b6a7817b408"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: 5b6a7817b408
|
||||
Revises: 7be5ef599d3f
|
||||
Create Date: 2026-07-02 11:57:13.095417
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '5b6a7817b408'
|
||||
down_revision: Union[str, None] = '7be5ef599d3f'
|
||||
revision: str = "5b6a7817b408"
|
||||
down_revision: Union[str, None] = "7be5ef599d3f"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: 70e48bda8829
|
||||
Revises: c7882cedc9ae
|
||||
Create Date: 2026-07-02 13:54:58.335394
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '70e48bda8829'
|
||||
down_revision: Union[str, None] = 'c7882cedc9ae'
|
||||
revision: str = "70e48bda8829"
|
||||
down_revision: Union[str, None] = "c7882cedc9ae"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: 7be5ef599d3f
|
||||
Revises: b60d0aa3ceda
|
||||
Create Date: 2026-06-25 15:29:20.685050
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '7be5ef599d3f'
|
||||
down_revision: Union[str, None] = 'b60d0aa3ceda'
|
||||
revision: str = "7be5ef599d3f"
|
||||
down_revision: Union[str, None] = "b60d0aa3ceda"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: 8bc411b5814e
|
||||
Revises: 0c3d1a5cff1f
|
||||
Create Date: 2026-07-02 12:37:59.457033
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '8bc411b5814e'
|
||||
down_revision: Union[str, None] = '0c3d1a5cff1f'
|
||||
revision: str = "8bc411b5814e"
|
||||
down_revision: Union[str, None] = "0c3d1a5cff1f"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,44 +4,49 @@ Revision ID: a192abbf4091
|
||||
Revises: 627d4460a94f
|
||||
Create Date: 2026-06-25 10:32:11.398529
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'a192abbf4091'
|
||||
down_revision: Union[str, None] = '627d4460a94f'
|
||||
revision: str = "a192abbf4091"
|
||||
down_revision: Union[str, None] = "627d4460a94f"
|
||||
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('manufacturers',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(), nullable=False),
|
||||
sa.Column('url', sa.String(), nullable=False),
|
||||
sa.Column('logo_url', sa.String(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
op.create_table(
|
||||
"manufacturers",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("url", sa.String(), nullable=False),
|
||||
sa.Column("logo_url", sa.String(), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
with op.batch_alter_table('unc_threads', schema=None) as batch_op:
|
||||
batch_op.alter_column('series',
|
||||
with op.batch_alter_table("unc_threads", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"series",
|
||||
existing_type=sa.VARCHAR(length=8),
|
||||
type_=sa.Enum('UNC', 'UNF', 'UNEF', 'UN', 'UNS', name='uncseries'),
|
||||
existing_nullable=False)
|
||||
type_=sa.Enum("UNC", "UNF", "UNEF", "UN", "UNS", name="uncseries"),
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('unc_threads', schema=None) as batch_op:
|
||||
batch_op.alter_column('series',
|
||||
existing_type=sa.Enum('UNC', 'UNF', 'UNEF', 'UN', 'UNS', name='uncseries'),
|
||||
with op.batch_alter_table("unc_threads", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"series",
|
||||
existing_type=sa.Enum("UNC", "UNF", "UNEF", "UN", "UNS", name="uncseries"),
|
||||
type_=sa.VARCHAR(length=8),
|
||||
existing_nullable=False)
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
op.drop_table('manufacturers')
|
||||
op.drop_table("manufacturers")
|
||||
# ### end Alembic commands ###
|
||||
@@ -4,33 +4,36 @@ Revision ID: a52e7ca7a925
|
||||
Revises: a192abbf4091
|
||||
Create Date: 2026-06-25 10:35:40.227253
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'a52e7ca7a925'
|
||||
down_revision: Union[str, None] = 'a192abbf4091'
|
||||
revision: str = "a52e7ca7a925"
|
||||
down_revision: Union[str, None] = "a192abbf4091"
|
||||
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('scope_models',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(), nullable=False),
|
||||
sa.Column('manufacturer_id', sa.Integer(), nullable=False),
|
||||
sa.Column('url', sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['manufacturer_id'], ['manufacturers.id'], ondelete='RESTRICT'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
op.create_table(
|
||||
"scope_models",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("manufacturer_id", sa.Integer(), nullable=False),
|
||||
sa.Column("url", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["manufacturer_id"], ["manufacturers.id"], ondelete="RESTRICT"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('scope_models')
|
||||
op.drop_table("scope_models")
|
||||
# ### end Alembic commands ###
|
||||
@@ -4,15 +4,15 @@ Revision ID: adf4a5b9493e
|
||||
Revises: 70e48bda8829
|
||||
Create Date: 2026-07-02 14:03:23.561253
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'adf4a5b9493e'
|
||||
down_revision: Union[str, None] = '70e48bda8829'
|
||||
revision: str = "adf4a5b9493e"
|
||||
down_revision: Union[str, None] = "70e48bda8829"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: b60d0aa3ceda
|
||||
Revises: fd5379986da4
|
||||
Create Date: 2026-06-25 11:29:20.420056
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'b60d0aa3ceda'
|
||||
down_revision: Union[str, None] = 'fd5379986da4'
|
||||
revision: str = "b60d0aa3ceda"
|
||||
down_revision: Union[str, None] = "fd5379986da4"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: bfbce324cd5f
|
||||
Revises: 8bc411b5814e
|
||||
Create Date: 2026-07-02 12:46:27.493942
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'bfbce324cd5f'
|
||||
down_revision: Union[str, None] = '8bc411b5814e'
|
||||
revision: str = "bfbce324cd5f"
|
||||
down_revision: Union[str, None] = "8bc411b5814e"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ Revision ID: c7882cedc9ae
|
||||
Revises: bfbce324cd5f
|
||||
Create Date: 2026-07-02 13:31:57.064381
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'c7882cedc9ae'
|
||||
down_revision: Union[str, None] = 'bfbce324cd5f'
|
||||
revision: str = "c7882cedc9ae"
|
||||
down_revision: Union[str, None] = "bfbce324cd5f"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -4,31 +4,32 @@ Revision ID: e23aa8dd2536
|
||||
Revises: a52e7ca7a925
|
||||
Create Date: 2026-06-25 10:49:56.463466
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'e23aa8dd2536'
|
||||
down_revision: Union[str, None] = 'a52e7ca7a925'
|
||||
revision: str = "e23aa8dd2536"
|
||||
down_revision: Union[str, None] = "a52e7ca7a925"
|
||||
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('part_types',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
op.create_table(
|
||||
"part_types",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("name"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('part_types')
|
||||
op.drop_table("part_types")
|
||||
# ### end Alembic commands ###
|
||||
@@ -4,15 +4,15 @@ Revision ID: fd5379986da4
|
||||
Revises: e23aa8dd2536
|
||||
Create Date: 2026-06-25 11:06:13.028067
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'fd5379986da4'
|
||||
down_revision: Union[str, None] = 'e23aa8dd2536'
|
||||
revision: str = "fd5379986da4"
|
||||
down_revision: Union[str, None] = "e23aa8dd2536"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
@@ -35,7 +35,10 @@ async def create_attachment_point_name(
|
||||
):
|
||||
apn = await apn_crud.create(db, name=payload.name)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="AttachmentPointName",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="AttachmentPointName",
|
||||
resource_id=apn.id,
|
||||
description=audit_crud.describe_create("AttachmentPointName", apn.id, apn.name),
|
||||
)
|
||||
@@ -67,7 +70,10 @@ async def update_attachment_point_name(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
apn = await apn_crud.update(db, apn, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="AttachmentPointName",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="AttachmentPointName",
|
||||
resource_id=apn.id,
|
||||
description=audit_crud.describe_update("AttachmentPointName", apn.id, changes),
|
||||
)
|
||||
@@ -86,7 +92,10 @@ async def delete_attachment_point_name(
|
||||
name = apn.name
|
||||
await apn_crud.delete(db, apn)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="AttachmentPointName",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="AttachmentPointName",
|
||||
resource_id=apn_id,
|
||||
description=audit_crud.describe_delete("AttachmentPointName", apn_id, name),
|
||||
)
|
||||
@@ -40,7 +40,9 @@ async def list_attachment_points(
|
||||
return await attachment_point_crud.list_all(db, skip=skip, limit=limit)
|
||||
|
||||
|
||||
@router.post("", response_model=AttachmentPointRead, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"", response_model=AttachmentPointRead, status_code=status.HTTP_201_CREATED
|
||||
)
|
||||
async def create_attachment_point(
|
||||
payload: AttachmentPointCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
@@ -59,7 +61,10 @@ async def create_attachment_point(
|
||||
)
|
||||
ap = await attachment_point_crud.create(db, **payload.model_dump())
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="AttachmentPoint",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="AttachmentPoint",
|
||||
resource_id=ap.id,
|
||||
description=audit_crud.describe_create(
|
||||
"AttachmentPoint", ap.id, _connection_label(ap)
|
||||
@@ -93,7 +98,10 @@ async def update_attachment_point(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
ap = await attachment_point_crud.update(db, ap, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="AttachmentPoint",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="AttachmentPoint",
|
||||
resource_id=ap.id,
|
||||
description=audit_crud.describe_update("AttachmentPoint", ap.id, changes),
|
||||
)
|
||||
@@ -112,7 +120,12 @@ async def delete_attachment_point(
|
||||
label = _connection_label(ap)
|
||||
await attachment_point_crud.delete(db, ap)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="AttachmentPoint",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="AttachmentPoint",
|
||||
resource_id=attachment_point_id,
|
||||
description=audit_crud.describe_delete("AttachmentPoint", attachment_point_id, label),
|
||||
description=audit_crud.describe_delete(
|
||||
"AttachmentPoint", attachment_point_id, label
|
||||
),
|
||||
)
|
||||
@@ -44,9 +44,14 @@ async def create_augmented_part(
|
||||
sub_part_type_id=payload.sub_part_type_id,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="AugmentedPart",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="AugmentedPart",
|
||||
resource_id=ap.id,
|
||||
description=audit_crud.describe_create("AugmentedPart", ap.id, ap.sku or ap.description),
|
||||
description=audit_crud.describe_create(
|
||||
"AugmentedPart", ap.id, ap.sku or ap.description
|
||||
),
|
||||
)
|
||||
return ap
|
||||
|
||||
@@ -58,7 +63,10 @@ async def sync_augmented_parts(
|
||||
):
|
||||
result = await augmented_part_sync.sync(db)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="AugmentedPart",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="AugmentedPart",
|
||||
resource_id=None,
|
||||
description=(
|
||||
f"Sync: {result.created} created, {result.deleted} deleted, "
|
||||
@@ -93,7 +101,10 @@ async def update_augmented_part(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
ap = await augmented_part_crud.update(db, ap, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="AugmentedPart",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="AugmentedPart",
|
||||
resource_id=ap.id,
|
||||
description=audit_crud.describe_update("AugmentedPart", ap.id, changes),
|
||||
)
|
||||
@@ -112,7 +123,12 @@ async def delete_augmented_part(
|
||||
label = ap.sku or ap.description
|
||||
await augmented_part_crud.delete(db, ap)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="AugmentedPart",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="AugmentedPart",
|
||||
resource_id=augmented_part_id,
|
||||
description=audit_crud.describe_delete("AugmentedPart", augmented_part_id, label),
|
||||
description=audit_crud.describe_delete(
|
||||
"AugmentedPart", augmented_part_id, label
|
||||
),
|
||||
)
|
||||
@@ -25,7 +25,9 @@ async def list_clamp_connections(
|
||||
return await clamp_connection_crud.list_all(db, skip=skip, limit=limit)
|
||||
|
||||
|
||||
@router.post("", response_model=ClampConnectionRead, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"", response_model=ClampConnectionRead, status_code=status.HTTP_201_CREATED
|
||||
)
|
||||
async def create_clamp_connection(
|
||||
payload: ClampConnectionCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
@@ -39,11 +41,19 @@ async def create_clamp_connection(
|
||||
max_in=payload.max_in,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="ClampConnection",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="ClampConnection",
|
||||
resource_id=cc.id,
|
||||
description=audit_crud.describe_create(
|
||||
"ClampConnection", cc.id,
|
||||
f"{cc.min_mm}–{cc.max_mm} mm" if cc.min_mm is not None else f"{cc.min_in}–{cc.max_in} in",
|
||||
"ClampConnection",
|
||||
cc.id,
|
||||
(
|
||||
f"{cc.min_mm}–{cc.max_mm} mm"
|
||||
if cc.min_mm is not None
|
||||
else f"{cc.min_in}–{cc.max_in} in"
|
||||
),
|
||||
),
|
||||
)
|
||||
return cc
|
||||
@@ -74,7 +84,10 @@ async def update_clamp_connection(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
cc = await clamp_connection_crud.update(db, cc, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="ClampConnection",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="ClampConnection",
|
||||
resource_id=cc.id,
|
||||
description=audit_crud.describe_update("ClampConnection", cc.id, changes),
|
||||
)
|
||||
@@ -91,12 +104,18 @@ async def delete_clamp_connection(
|
||||
if cc is None:
|
||||
raise HTTPException(status_code=404, detail="Clamp connection not found")
|
||||
label = (
|
||||
f"{cc.min_mm}–{cc.max_mm} mm" if cc.min_mm is not None
|
||||
f"{cc.min_mm}–{cc.max_mm} mm"
|
||||
if cc.min_mm is not None
|
||||
else f"{cc.min_in}–{cc.max_in} in"
|
||||
)
|
||||
await clamp_connection_crud.delete(db, cc)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="ClampConnection",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="ClampConnection",
|
||||
resource_id=clamp_connection_id,
|
||||
description=audit_crud.describe_delete("ClampConnection", clamp_connection_id, label),
|
||||
description=audit_crud.describe_delete(
|
||||
"ClampConnection", clamp_connection_id, label
|
||||
),
|
||||
)
|
||||
@@ -51,7 +51,11 @@ async def update_config(
|
||||
):
|
||||
current = await config_crud.get(db)
|
||||
# Keep existing password if not provided in the update
|
||||
new_password = payload.password if payload.password is not None else (current.password if current else None)
|
||||
new_password = (
|
||||
payload.password
|
||||
if payload.password is not None
|
||||
else (current.password if current else None)
|
||||
)
|
||||
config = await config_crud.upsert(
|
||||
db,
|
||||
odata_url=payload.odata_url,
|
||||
@@ -59,7 +63,10 @@ async def update_config(
|
||||
password=new_password,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="ImportedPartConfig",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="ImportedPartConfig",
|
||||
resource_id=1,
|
||||
description="Updated ImportedPart OData configuration",
|
||||
)
|
||||
@@ -81,10 +88,15 @@ async def refresh_imported_parts(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="OData URL is not configured.",
|
||||
)
|
||||
records = await odata_service.fetch(config.odata_url, config.username, config.password)
|
||||
records = await odata_service.fetch(
|
||||
config.odata_url, config.username, config.password
|
||||
)
|
||||
created, updated = await imported_part_crud.upsert_bulk(db, records)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="ImportedPart",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="ImportedPart",
|
||||
resource_id=None,
|
||||
description=f"OData sync: {created} created, {updated} updated ({len(records)} total records)",
|
||||
)
|
||||
@@ -99,7 +111,9 @@ async def get_imported_part(
|
||||
):
|
||||
part = await imported_part_crud.get(db, inventory_id)
|
||||
if part is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Imported part not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Imported part not found"
|
||||
)
|
||||
return part
|
||||
|
||||
|
||||
@@ -111,10 +125,15 @@ async def delete_imported_part(
|
||||
):
|
||||
part = await imported_part_crud.get(db, inventory_id)
|
||||
if part is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Imported part not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Imported part not found"
|
||||
)
|
||||
await imported_part_crud.delete(db, part)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="ImportedPart",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="ImportedPart",
|
||||
resource_id=None,
|
||||
description=audit_crud.describe_delete("ImportedPart", 0, inventory_id),
|
||||
)
|
||||
+24
-7
@@ -34,9 +34,14 @@ async def create_iso_thread(
|
||||
depth_mm=payload.depth_mm,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="IsoThread",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="IsoThread",
|
||||
resource_id=thread.id,
|
||||
description=audit_crud.describe_create("IsoThread", thread.id, f"M{thread.diameter_mm}×{thread.pitch_mm}"),
|
||||
description=audit_crud.describe_create(
|
||||
"IsoThread", thread.id, f"M{thread.diameter_mm}×{thread.pitch_mm}"
|
||||
),
|
||||
)
|
||||
return thread
|
||||
|
||||
@@ -49,7 +54,9 @@ async def get_iso_thread(
|
||||
):
|
||||
thread = await iso_thread_crud.get(db, iso_thread_id)
|
||||
if thread is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="ISO thread not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="ISO thread not found"
|
||||
)
|
||||
return thread
|
||||
|
||||
|
||||
@@ -62,11 +69,16 @@ async def update_iso_thread(
|
||||
):
|
||||
thread = await iso_thread_crud.get(db, iso_thread_id)
|
||||
if thread is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="ISO thread not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="ISO thread not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
thread = await iso_thread_crud.update(db, thread, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="IsoThread",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="IsoThread",
|
||||
resource_id=thread.id,
|
||||
description=audit_crud.describe_update("IsoThread", thread.id, changes),
|
||||
)
|
||||
@@ -81,11 +93,16 @@ async def delete_iso_thread(
|
||||
):
|
||||
thread = await iso_thread_crud.get(db, iso_thread_id)
|
||||
if thread is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="ISO thread not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="ISO thread not found"
|
||||
)
|
||||
name = f"M{thread.diameter_mm}×{thread.pitch_mm}"
|
||||
await iso_thread_crud.delete(db, thread)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="IsoThread",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="IsoThread",
|
||||
resource_id=iso_thread_id,
|
||||
description=audit_crud.describe_delete("IsoThread", iso_thread_id, name),
|
||||
)
|
||||
@@ -6,7 +6,11 @@ from app.crud import audit as audit_crud
|
||||
from app.crud import manufacturer as manufacturer_crud
|
||||
from app.deps import require_permission
|
||||
from app.models.user import User
|
||||
from app.schemas.manufacturer import ManufacturerCreate, ManufacturerRead, ManufacturerUpdate
|
||||
from app.schemas.manufacturer import (
|
||||
ManufacturerCreate,
|
||||
ManufacturerRead,
|
||||
ManufacturerUpdate,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -34,7 +38,10 @@ async def create_manufacturer(
|
||||
logo_url=str(payload.logo_url) if payload.logo_url else None,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="Manufacturer",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="Manufacturer",
|
||||
resource_id=m.id,
|
||||
description=audit_crud.describe_create("Manufacturer", m.id, m.name),
|
||||
)
|
||||
@@ -49,7 +56,9 @@ async def get_manufacturer(
|
||||
):
|
||||
m = await manufacturer_crud.get(db, manufacturer_id)
|
||||
if m is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Manufacturer not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Manufacturer not found"
|
||||
)
|
||||
return m
|
||||
|
||||
|
||||
@@ -62,7 +71,9 @@ async def update_manufacturer(
|
||||
):
|
||||
m = await manufacturer_crud.get(db, manufacturer_id)
|
||||
if m is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Manufacturer not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Manufacturer not found"
|
||||
)
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
if "url" in data and data["url"] is not None:
|
||||
data["url"] = str(data["url"])
|
||||
@@ -70,7 +81,10 @@ async def update_manufacturer(
|
||||
data["logo_url"] = str(data["logo_url"])
|
||||
m = await manufacturer_crud.update(db, m, data)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="Manufacturer",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="Manufacturer",
|
||||
resource_id=m.id,
|
||||
description=audit_crud.describe_update("Manufacturer", m.id, data),
|
||||
)
|
||||
@@ -85,11 +99,16 @@ async def delete_manufacturer(
|
||||
):
|
||||
m = await manufacturer_crud.get(db, manufacturer_id)
|
||||
if m is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Manufacturer not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Manufacturer not found"
|
||||
)
|
||||
name = m.name
|
||||
await manufacturer_crud.delete(db, m)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="Manufacturer",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="Manufacturer",
|
||||
resource_id=manufacturer_id,
|
||||
description=audit_crud.describe_delete("Manufacturer", manufacturer_id, name),
|
||||
)
|
||||
+12
-3
@@ -29,7 +29,10 @@ async def create_part_colour(
|
||||
):
|
||||
pc = await part_colour_crud.create(db, name=payload.name)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="PartColour",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="PartColour",
|
||||
resource_id=pc.id,
|
||||
description=audit_crud.describe_create("PartColour", pc.id, pc.name),
|
||||
)
|
||||
@@ -61,7 +64,10 @@ async def update_part_colour(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
pc = await part_colour_crud.update(db, pc, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="PartColour",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="PartColour",
|
||||
resource_id=pc.id,
|
||||
description=audit_crud.describe_update("PartColour", pc.id, changes),
|
||||
)
|
||||
@@ -80,7 +86,10 @@ async def delete_part_colour(
|
||||
name = pc.name
|
||||
await part_colour_crud.delete(db, pc)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="PartColour",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="PartColour",
|
||||
resource_id=part_colour_id,
|
||||
description=audit_crud.describe_delete("PartColour", part_colour_id, name),
|
||||
)
|
||||
+21
-6
@@ -29,7 +29,10 @@ async def create_part_type(
|
||||
):
|
||||
pt = await part_type_crud.create(db, name=payload.name)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="PartType",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="PartType",
|
||||
resource_id=pt.id,
|
||||
description=audit_crud.describe_create("PartType", pt.id, pt.name),
|
||||
)
|
||||
@@ -44,7 +47,9 @@ async def get_part_type(
|
||||
):
|
||||
pt = await part_type_crud.get(db, part_type_id)
|
||||
if pt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found"
|
||||
)
|
||||
return pt
|
||||
|
||||
|
||||
@@ -57,11 +62,16 @@ async def update_part_type(
|
||||
):
|
||||
pt = await part_type_crud.get(db, part_type_id)
|
||||
if pt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
pt = await part_type_crud.update(db, pt, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="PartType",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="PartType",
|
||||
resource_id=pt.id,
|
||||
description=audit_crud.describe_update("PartType", pt.id, changes),
|
||||
)
|
||||
@@ -76,11 +86,16 @@ async def delete_part_type(
|
||||
):
|
||||
pt = await part_type_crud.get(db, part_type_id)
|
||||
if pt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Part type not found"
|
||||
)
|
||||
name = pt.name
|
||||
await part_type_crud.delete(db, pt)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="PartType",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="PartType",
|
||||
resource_id=part_type_id,
|
||||
description=audit_crud.describe_delete("PartType", part_type_id, name),
|
||||
)
|
||||
+12
-3
@@ -36,7 +36,10 @@ async def create_role(
|
||||
permission_ids=payload.permission_ids,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="Role",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="Role",
|
||||
resource_id=role.id,
|
||||
description=audit_crud.describe_create("Role", role.id, role.name),
|
||||
)
|
||||
@@ -80,7 +83,10 @@ async def update_role(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
role = await role_crud.update(db, role, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="Role",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="Role",
|
||||
resource_id=role.id,
|
||||
description=audit_crud.describe_update("Role", role.id, changes),
|
||||
)
|
||||
@@ -101,7 +107,10 @@ async def delete_role(
|
||||
name = role.name
|
||||
await role_crud.delete(db, role)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="Role",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="Role",
|
||||
resource_id=role_id,
|
||||
description=audit_crud.describe_delete("Role", role_id, name),
|
||||
)
|
||||
+57
-13
@@ -1,22 +1,66 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api import attachment_point_names, attachment_points, audit_log, augmented_parts, auth, clamp_connections, imported_parts, iso_threads, manufacturers, part_colours, part_types, roles, scope_models, static_connections, sub_part_types, unc_threads, users
|
||||
from app.api import (
|
||||
attachment_point_names,
|
||||
attachment_points,
|
||||
audit_log,
|
||||
augmented_parts,
|
||||
auth,
|
||||
clamp_connections,
|
||||
imported_parts,
|
||||
iso_threads,
|
||||
manufacturers,
|
||||
part_colours,
|
||||
part_types,
|
||||
roles,
|
||||
scope_models,
|
||||
static_connections,
|
||||
sub_part_types,
|
||||
unc_threads,
|
||||
users,
|
||||
)
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
|
||||
api_router.include_router(users.router, prefix="/users", tags=["users"])
|
||||
api_router.include_router(roles.router, prefix="/roles", tags=["roles"])
|
||||
api_router.include_router(iso_threads.router, prefix="/iso-threads", tags=["iso-threads"])
|
||||
api_router.include_router(unc_threads.router, prefix="/unc-threads", tags=["unc-threads"])
|
||||
api_router.include_router(manufacturers.router, prefix="/manufacturers", tags=["manufacturers"])
|
||||
api_router.include_router(scope_models.router, prefix="/scope-models", tags=["scope-models"])
|
||||
api_router.include_router(part_colours.router, prefix="/part-colours", tags=["part-colours"])
|
||||
api_router.include_router(
|
||||
iso_threads.router, prefix="/iso-threads", tags=["iso-threads"]
|
||||
)
|
||||
api_router.include_router(
|
||||
unc_threads.router, prefix="/unc-threads", tags=["unc-threads"]
|
||||
)
|
||||
api_router.include_router(
|
||||
manufacturers.router, prefix="/manufacturers", tags=["manufacturers"]
|
||||
)
|
||||
api_router.include_router(
|
||||
scope_models.router, prefix="/scope-models", tags=["scope-models"]
|
||||
)
|
||||
api_router.include_router(
|
||||
part_colours.router, prefix="/part-colours", tags=["part-colours"]
|
||||
)
|
||||
api_router.include_router(part_types.router, prefix="/part-types", tags=["part-types"])
|
||||
api_router.include_router(sub_part_types.router, prefix="/sub-part-types", tags=["sub-part-types"])
|
||||
api_router.include_router(static_connections.router, prefix="/static-connections", tags=["static-connections"])
|
||||
api_router.include_router(clamp_connections.router, prefix="/clamp-connections", tags=["clamp-connections"])
|
||||
api_router.include_router(attachment_point_names.router, prefix="/attachment-point-names", tags=["attachment-point-names"])
|
||||
api_router.include_router(attachment_points.router, prefix="/attachment-points", tags=["attachment-points"])
|
||||
api_router.include_router(imported_parts.router, prefix="/imported-parts", tags=["imported-parts"])
|
||||
api_router.include_router(augmented_parts.router, prefix="/augmented-parts", tags=["augmented-parts"])
|
||||
api_router.include_router(
|
||||
sub_part_types.router, prefix="/sub-part-types", tags=["sub-part-types"]
|
||||
)
|
||||
api_router.include_router(
|
||||
static_connections.router, prefix="/static-connections", tags=["static-connections"]
|
||||
)
|
||||
api_router.include_router(
|
||||
clamp_connections.router, prefix="/clamp-connections", tags=["clamp-connections"]
|
||||
)
|
||||
api_router.include_router(
|
||||
attachment_point_names.router,
|
||||
prefix="/attachment-point-names",
|
||||
tags=["attachment-point-names"],
|
||||
)
|
||||
api_router.include_router(
|
||||
attachment_points.router, prefix="/attachment-points", tags=["attachment-points"]
|
||||
)
|
||||
api_router.include_router(
|
||||
imported_parts.router, prefix="/imported-parts", tags=["imported-parts"]
|
||||
)
|
||||
api_router.include_router(
|
||||
augmented_parts.router, prefix="/augmented-parts", tags=["augmented-parts"]
|
||||
)
|
||||
api_router.include_router(audit_log.router, prefix="/audit-log", tags=["audit-log"])
|
||||
+21
-6
@@ -34,7 +34,10 @@ async def create_scope_model(
|
||||
url=str(payload.url) if payload.url else None,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="ScopeModel",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="ScopeModel",
|
||||
resource_id=sm.id,
|
||||
description=audit_crud.describe_create("ScopeModel", sm.id, sm.name),
|
||||
)
|
||||
@@ -49,7 +52,9 @@ async def get_scope_model(
|
||||
):
|
||||
sm = await scope_model_crud.get(db, scope_model_id)
|
||||
if sm is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Scope model not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Scope model not found"
|
||||
)
|
||||
return sm
|
||||
|
||||
|
||||
@@ -62,13 +67,18 @@ async def update_scope_model(
|
||||
):
|
||||
sm = await scope_model_crud.get(db, scope_model_id)
|
||||
if sm is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Scope model not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Scope model not found"
|
||||
)
|
||||
data = payload.model_dump(exclude_unset=True)
|
||||
if "url" in data and data["url"] is not None:
|
||||
data["url"] = str(data["url"])
|
||||
sm = await scope_model_crud.update(db, sm, data)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="ScopeModel",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="ScopeModel",
|
||||
resource_id=sm.id,
|
||||
description=audit_crud.describe_update("ScopeModel", sm.id, data),
|
||||
)
|
||||
@@ -83,11 +93,16 @@ async def delete_scope_model(
|
||||
):
|
||||
sm = await scope_model_crud.get(db, scope_model_id)
|
||||
if sm is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Scope model not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Scope model not found"
|
||||
)
|
||||
name = sm.name
|
||||
await scope_model_crud.delete(db, sm)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="ScopeModel",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="ScopeModel",
|
||||
resource_id=scope_model_id,
|
||||
description=audit_crud.describe_delete("ScopeModel", scope_model_id, name),
|
||||
)
|
||||
@@ -25,17 +25,26 @@ async def list_static_connections(
|
||||
return await static_connection_crud.list_all(db, skip=skip, limit=limit)
|
||||
|
||||
|
||||
@router.post("", response_model=StaticConnectionRead, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"", response_model=StaticConnectionRead, status_code=status.HTTP_201_CREATED
|
||||
)
|
||||
async def create_static_connection(
|
||||
payload: StaticConnectionCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(require_permission("static_connections:write")),
|
||||
):
|
||||
sc = await static_connection_crud.create(db, od_mm=payload.od_mm, od_in=payload.od_in)
|
||||
sc = await static_connection_crud.create(
|
||||
db, od_mm=payload.od_mm, od_in=payload.od_in
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="StaticConnection",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="StaticConnection",
|
||||
resource_id=sc.id,
|
||||
description=audit_crud.describe_create("StaticConnection", sc.id, f"{sc.od_mm} mm"),
|
||||
description=audit_crud.describe_create(
|
||||
"StaticConnection", sc.id, f"{sc.od_mm} mm"
|
||||
),
|
||||
)
|
||||
return sc
|
||||
|
||||
@@ -65,7 +74,10 @@ async def update_static_connection(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
sc = await static_connection_crud.update(db, sc, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="StaticConnection",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="StaticConnection",
|
||||
resource_id=sc.id,
|
||||
description=audit_crud.describe_update("StaticConnection", sc.id, changes),
|
||||
)
|
||||
@@ -84,7 +96,10 @@ async def delete_static_connection(
|
||||
od_mm = sc.od_mm
|
||||
await static_connection_crud.delete(db, sc)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="StaticConnection",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="StaticConnection",
|
||||
resource_id=static_connection_id,
|
||||
description=audit_crud.describe_delete(
|
||||
"StaticConnection", static_connection_id, f"{od_mm} mm"
|
||||
|
||||
@@ -6,7 +6,11 @@ from app.crud import audit as audit_crud
|
||||
from app.crud import sub_part_type as sub_part_type_crud
|
||||
from app.deps import require_permission
|
||||
from app.models.user import User
|
||||
from app.schemas.sub_part_type import SubPartTypeCreate, SubPartTypeRead, SubPartTypeUpdate
|
||||
from app.schemas.sub_part_type import (
|
||||
SubPartTypeCreate,
|
||||
SubPartTypeRead,
|
||||
SubPartTypeUpdate,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -31,7 +35,10 @@ async def create_sub_part_type(
|
||||
db, name=payload.name, part_type_id=payload.part_type_id
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="SubPartType",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="SubPartType",
|
||||
resource_id=spt.id,
|
||||
description=audit_crud.describe_create("SubPartType", spt.id, spt.name),
|
||||
)
|
||||
@@ -46,7 +53,9 @@ async def get_sub_part_type(
|
||||
):
|
||||
spt = await sub_part_type_crud.get(db, sub_part_type_id)
|
||||
if spt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found"
|
||||
)
|
||||
return spt
|
||||
|
||||
|
||||
@@ -59,11 +68,16 @@ async def update_sub_part_type(
|
||||
):
|
||||
spt = await sub_part_type_crud.get(db, sub_part_type_id)
|
||||
if spt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
spt = await sub_part_type_crud.update(db, spt, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="SubPartType",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="SubPartType",
|
||||
resource_id=spt.id,
|
||||
description=audit_crud.describe_update("SubPartType", spt.id, changes),
|
||||
)
|
||||
@@ -78,11 +92,16 @@ async def delete_sub_part_type(
|
||||
):
|
||||
spt = await sub_part_type_crud.get(db, sub_part_type_id)
|
||||
if spt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Sub part type not found"
|
||||
)
|
||||
name = spt.name
|
||||
await sub_part_type_crud.delete(db, spt)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="SubPartType",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="SubPartType",
|
||||
resource_id=sub_part_type_id,
|
||||
description=audit_crud.describe_delete("SubPartType", sub_part_type_id, name),
|
||||
)
|
||||
+26
-7
@@ -35,9 +35,16 @@ async def create_unc_thread(
|
||||
depth_in=payload.depth_in,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="UncThread",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="UncThread",
|
||||
resource_id=thread.id,
|
||||
description=audit_crud.describe_create("UncThread", thread.id, f"{thread.diameter_fraction}-{thread.tpi} {thread.series.value}"),
|
||||
description=audit_crud.describe_create(
|
||||
"UncThread",
|
||||
thread.id,
|
||||
f"{thread.diameter_fraction}-{thread.tpi} {thread.series.value}",
|
||||
),
|
||||
)
|
||||
return thread
|
||||
|
||||
@@ -50,7 +57,9 @@ async def get_unc_thread(
|
||||
):
|
||||
thread = await unc_thread_crud.get(db, unc_thread_id)
|
||||
if thread is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="UNC thread not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="UNC thread not found"
|
||||
)
|
||||
return thread
|
||||
|
||||
|
||||
@@ -63,11 +72,16 @@ async def update_unc_thread(
|
||||
):
|
||||
thread = await unc_thread_crud.get(db, unc_thread_id)
|
||||
if thread is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="UNC thread not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="UNC thread not found"
|
||||
)
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
thread = await unc_thread_crud.update(db, thread, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="UncThread",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="UncThread",
|
||||
resource_id=thread.id,
|
||||
description=audit_crud.describe_update("UncThread", thread.id, changes),
|
||||
)
|
||||
@@ -82,11 +96,16 @@ async def delete_unc_thread(
|
||||
):
|
||||
thread = await unc_thread_crud.get(db, unc_thread_id)
|
||||
if thread is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="UNC thread not found")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="UNC thread not found"
|
||||
)
|
||||
name = f"{thread.diameter_fraction}-{thread.tpi} {thread.series.value}"
|
||||
await unc_thread_crud.delete(db, thread)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="UncThread",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="UncThread",
|
||||
resource_id=unc_thread_id,
|
||||
description=audit_crud.describe_delete("UncThread", unc_thread_id, name),
|
||||
)
|
||||
+12
-3
@@ -45,7 +45,10 @@ async def create_user(
|
||||
role_ids=payload.role_ids,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="create", resource_type="User",
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="User",
|
||||
resource_id=created.id,
|
||||
description=audit_crud.describe_create("User", created.id, created.email),
|
||||
)
|
||||
@@ -81,7 +84,10 @@ async def update_user(
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
target = await user_crud.update(db, target, changes)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="update", resource_type="User",
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="User",
|
||||
resource_id=user_id,
|
||||
description=audit_crud.describe_update("User", user_id, changes),
|
||||
)
|
||||
@@ -102,7 +108,10 @@ async def delete_user(
|
||||
email = target.email
|
||||
await user_crud.delete(db, target)
|
||||
await audit_crud.log(
|
||||
db, actor=actor, action="delete", resource_type="User",
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="User",
|
||||
resource_id=user_id,
|
||||
description=audit_crud.describe_delete("User", user_id, email),
|
||||
)
|
||||
@@ -63,9 +63,7 @@ async def create(
|
||||
return ap
|
||||
|
||||
|
||||
async def update(
|
||||
db: AsyncSession, ap: AttachmentPoint, data: dict
|
||||
) -> AttachmentPoint:
|
||||
async def update(db: AsyncSession, ap: AttachmentPoint, data: dict) -> AttachmentPoint:
|
||||
for field, value in data.items():
|
||||
setattr(ap, field, value)
|
||||
await db.commit()
|
||||
|
||||
@@ -48,7 +48,9 @@ async def list_filtered(
|
||||
q = q.where(AugmentedPart.part_type_id == part_type_id)
|
||||
if sub_part_type_id is not None:
|
||||
q = q.where(AugmentedPart.sub_part_type_id == sub_part_type_id)
|
||||
result = await db.execute(q.order_by(AugmentedPart.description).offset(skip).limit(limit))
|
||||
result = await db.execute(
|
||||
q.order_by(AugmentedPart.description).offset(skip).limit(limit)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@@ -62,6 +64,7 @@ async def count_filtered(
|
||||
sub_part_type_id: int | None = None,
|
||||
) -> int:
|
||||
from sqlalchemy import func
|
||||
|
||||
q = select(func.count()).select_from(AugmentedPart)
|
||||
if description.strip():
|
||||
q = q.where(AugmentedPart.description.ilike(f"%{description.strip()}%"))
|
||||
@@ -86,19 +89,18 @@ async def get_unlinked_imported_parts_filtered(
|
||||
skip: int = 0,
|
||||
limit: int = 50,
|
||||
) -> list[ImportedPart]:
|
||||
q = (
|
||||
select(ImportedPart)
|
||||
.where(
|
||||
q = select(ImportedPart).where(
|
||||
ImportedPart.InventoryID.not_in(
|
||||
select(augmented_part_imported_parts.c.imported_part_id)
|
||||
)
|
||||
)
|
||||
)
|
||||
if description.strip():
|
||||
q = q.where(ImportedPart.Description.ilike(f"%{description.strip()}%"))
|
||||
if colour.strip():
|
||||
q = q.where(ImportedPart.TBXColour.ilike(f"%{colour.strip()}%"))
|
||||
result = await db.execute(q.order_by(ImportedPart.InventoryID).offset(skip).limit(limit))
|
||||
result = await db.execute(
|
||||
q.order_by(ImportedPart.InventoryID).offset(skip).limit(limit)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@@ -108,6 +110,7 @@ async def count_unlinked_filtered(
|
||||
colour: str = "",
|
||||
) -> int:
|
||||
from sqlalchemy import func
|
||||
|
||||
q = (
|
||||
select(func.count())
|
||||
.select_from(ImportedPart)
|
||||
@@ -166,9 +169,7 @@ async def create(
|
||||
return ap
|
||||
|
||||
|
||||
async def update(
|
||||
db: AsyncSession, ap: AugmentedPart, data: dict
|
||||
) -> AugmentedPart:
|
||||
async def update(db: AsyncSession, ap: AugmentedPart, data: dict) -> AugmentedPart:
|
||||
for field, value in data.items():
|
||||
setattr(ap, field, value)
|
||||
await db.commit()
|
||||
|
||||
@@ -13,7 +13,9 @@ async def list_all(
|
||||
) -> list[ClampConnection]:
|
||||
result = await db.execute(
|
||||
select(ClampConnection)
|
||||
.order_by(ClampConnection.min_mm.nulls_last(), ClampConnection.min_in.nulls_last())
|
||||
.order_by(
|
||||
ClampConnection.min_mm.nulls_last(), ClampConnection.min_in.nulls_last()
|
||||
)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
@@ -35,9 +37,7 @@ async def create(
|
||||
return cc
|
||||
|
||||
|
||||
async def update(
|
||||
db: AsyncSession, cc: ClampConnection, data: dict
|
||||
) -> ClampConnection:
|
||||
async def update(db: AsyncSession, cc: ClampConnection, data: dict) -> ClampConnection:
|
||||
for field, value in data.items():
|
||||
setattr(cc, field, value)
|
||||
await db.commit()
|
||||
|
||||
@@ -3,11 +3,18 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.imported_part import ImportedPart
|
||||
|
||||
_FIELDS = frozenset({
|
||||
"InventoryID", "Description", "TBXColour",
|
||||
"ProductMaxThreadDepthmm", "ProductMaxThreadDepthin",
|
||||
"ProductODmm", "ProductODin", "TBXThreadAttribute",
|
||||
})
|
||||
_FIELDS = frozenset(
|
||||
{
|
||||
"InventoryID",
|
||||
"Description",
|
||||
"TBXColour",
|
||||
"ProductMaxThreadDepthmm",
|
||||
"ProductMaxThreadDepthin",
|
||||
"ProductODmm",
|
||||
"ProductODin",
|
||||
"TBXThreadAttribute",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def get(db: AsyncSession, inventory_id: str) -> ImportedPart | None:
|
||||
@@ -18,7 +25,10 @@ async def list_all(
|
||||
db: AsyncSession, skip: int = 0, limit: int = 500
|
||||
) -> list[ImportedPart]:
|
||||
result = await db.execute(
|
||||
select(ImportedPart).order_by(ImportedPart.InventoryID).offset(skip).limit(limit)
|
||||
select(ImportedPart)
|
||||
.order_by(ImportedPart.InventoryID)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
@@ -32,7 +42,9 @@ async def list_filtered(
|
||||
q = select(ImportedPart)
|
||||
if description.strip():
|
||||
q = q.where(ImportedPart.Description.ilike(f"%{description.strip()}%"))
|
||||
result = await db.execute(q.order_by(ImportedPart.InventoryID).offset(skip).limit(limit))
|
||||
result = await db.execute(
|
||||
q.order_by(ImportedPart.InventoryID).offset(skip).limit(limit)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@@ -47,12 +59,14 @@ async def distinct_colours(db: AsyncSession) -> list[str]:
|
||||
|
||||
async def count(db: AsyncSession) -> int:
|
||||
from sqlalchemy import func
|
||||
|
||||
result = await db.execute(select(func.count()).select_from(ImportedPart))
|
||||
return result.scalar_one()
|
||||
|
||||
|
||||
async def count_filtered(db: AsyncSession, description: str = "") -> int:
|
||||
from sqlalchemy import func
|
||||
|
||||
q = select(func.count()).select_from(ImportedPart)
|
||||
if description.strip():
|
||||
q = q.where(ImportedPart.Description.ilike(f"%{description.strip()}%"))
|
||||
@@ -81,7 +95,9 @@ async def upsert_bulk(db: AsyncSession, records: list[dict]) -> tuple[int, int]:
|
||||
incoming_ids = {r["InventoryID"] for r in clean}
|
||||
|
||||
result = await db.execute(
|
||||
select(ImportedPart.InventoryID).where(ImportedPart.InventoryID.in_(incoming_ids))
|
||||
select(ImportedPart.InventoryID).where(
|
||||
ImportedPart.InventoryID.in_(incoming_ids)
|
||||
)
|
||||
)
|
||||
existing_ids = {row[0] for row in result.all()}
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ async def get_by_values(
|
||||
depth_mm: float | None,
|
||||
) -> IsoThread | None:
|
||||
depth_clause = (
|
||||
IsoThread.depth_mm.is_(None) if depth_mm is None else IsoThread.depth_mm == depth_mm
|
||||
IsoThread.depth_mm.is_(None)
|
||||
if depth_mm is None
|
||||
else IsoThread.depth_mm == depth_mm
|
||||
)
|
||||
result = await db.execute(
|
||||
select(IsoThread).where(
|
||||
@@ -28,7 +30,9 @@ async def get_by_values(
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_all(db: AsyncSession, skip: int = 0, limit: int = 100) -> list[IsoThread]:
|
||||
async def list_all(
|
||||
db: AsyncSession, skip: int = 0, limit: int = 100
|
||||
) -> list[IsoThread]:
|
||||
result = await db.execute(
|
||||
select(IsoThread).order_by(IsoThread.id).offset(skip).limit(limit)
|
||||
)
|
||||
|
||||
@@ -9,9 +9,7 @@ async def get(db: AsyncSession, manufacturer_id: int) -> Manufacturer | None:
|
||||
|
||||
|
||||
async def get_by_name(db: AsyncSession, name: str) -> Manufacturer | None:
|
||||
result = await db.execute(
|
||||
select(Manufacturer).where(Manufacturer.name == name)
|
||||
)
|
||||
result = await db.execute(select(Manufacturer).where(Manufacturer.name == name))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ async def get(db: AsyncSession, part_colour_id: int) -> PartColour | None:
|
||||
|
||||
|
||||
async def get_by_name(db: AsyncSession, name: str) -> PartColour | None:
|
||||
result = await db.execute(
|
||||
select(PartColour).where(PartColour.name == name)
|
||||
)
|
||||
result = await db.execute(select(PartColour).where(PartColour.name == name))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ async def get_by_name(db: AsyncSession, name: str) -> PartType | None:
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_all(
|
||||
db: AsyncSession, skip: int = 0, limit: int = 100
|
||||
) -> list[PartType]:
|
||||
async def list_all(db: AsyncSession, skip: int = 0, limit: int = 100) -> list[PartType]:
|
||||
result = await db.execute(
|
||||
select(PartType).order_by(PartType.name).offset(skip).limit(limit)
|
||||
)
|
||||
|
||||
@@ -19,7 +19,10 @@ async def list_all(
|
||||
db: AsyncSession, skip: int = 0, limit: int = 100
|
||||
) -> list[StaticConnection]:
|
||||
result = await db.execute(
|
||||
select(StaticConnection).order_by(StaticConnection.od_mm).offset(skip).limit(limit)
|
||||
select(StaticConnection)
|
||||
.order_by(StaticConnection.od_mm)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ async def list_all(
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
async def create(
|
||||
db: AsyncSession, *, name: str, part_type_id: int
|
||||
) -> SubPartType:
|
||||
async def create(db: AsyncSession, *, name: str, part_type_id: int) -> SubPartType:
|
||||
spt = SubPartType(name=name, part_type_id=part_type_id)
|
||||
db.add(spt)
|
||||
await db.commit()
|
||||
|
||||
@@ -17,7 +17,9 @@ async def get_by_values(
|
||||
depth_in: float | None,
|
||||
) -> UncThread | None:
|
||||
depth_clause = (
|
||||
UncThread.depth_in.is_(None) if depth_in is None else UncThread.depth_in == depth_in
|
||||
UncThread.depth_in.is_(None)
|
||||
if depth_in is None
|
||||
else UncThread.depth_in == depth_in
|
||||
)
|
||||
result = await db.execute(
|
||||
select(UncThread).where(
|
||||
@@ -30,7 +32,9 @@ async def get_by_values(
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def list_all(db: AsyncSession, skip: int = 0, limit: int = 100) -> list[UncThread]:
|
||||
async def list_all(
|
||||
db: AsyncSession, skip: int = 0, limit: int = 100
|
||||
) -> list[UncThread]:
|
||||
result = await db.execute(
|
||||
select(UncThread).order_by(UncThread.id).offset(skip).limit(limit)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
"""Import all models so they register with ``Base.metadata``."""
|
||||
|
||||
from app.models.associations import augmented_part_imported_parts, role_permissions, user_roles
|
||||
from app.models.associations import (
|
||||
augmented_part_imported_parts,
|
||||
role_permissions,
|
||||
user_roles,
|
||||
)
|
||||
from app.models.attachment_point import AttachmentPoint
|
||||
from app.models.attachment_point_name import AttachmentPointName
|
||||
from app.models.audit_log import AuditLog
|
||||
|
||||
@@ -29,10 +29,14 @@ class AttachmentPoint(Base):
|
||||
ForeignKey("unc_threads.id", ondelete="RESTRICT"), nullable=True, default=None
|
||||
)
|
||||
clamp_connection_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("clamp_connections.id", ondelete="RESTRICT"), nullable=True, default=None
|
||||
ForeignKey("clamp_connections.id", ondelete="RESTRICT"),
|
||||
nullable=True,
|
||||
default=None,
|
||||
)
|
||||
static_connection_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("static_connections.id", ondelete="RESTRICT"), nullable=True, default=None
|
||||
ForeignKey("static_connections.id", ondelete="RESTRICT"),
|
||||
nullable=True,
|
||||
default=None,
|
||||
)
|
||||
|
||||
attachment_point_name: Mapped[AttachmentPointName] = relationship(lazy="selectin")
|
||||
|
||||
@@ -30,7 +30,9 @@ class AugmentedPart(Base):
|
||||
ForeignKey("part_types.id", ondelete="RESTRICT"), nullable=False
|
||||
)
|
||||
sub_part_type_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("sub_part_types.id", ondelete="SET NULL"), nullable=True, default=None
|
||||
ForeignKey("sub_part_types.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
default=None,
|
||||
)
|
||||
|
||||
# linked imported parts — not eagerly loaded; used by sync service only
|
||||
|
||||
@@ -10,8 +10,12 @@ class ImportedPart(Base):
|
||||
InventoryID: Mapped[str] = mapped_column(String, primary_key=True)
|
||||
Description: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
TBXColour: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
ProductMaxThreadDepthmm: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
ProductMaxThreadDepthin: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
ProductMaxThreadDepthmm: Mapped[str | None] = mapped_column(
|
||||
nullable=True, default=None
|
||||
)
|
||||
ProductMaxThreadDepthin: Mapped[str | None] = mapped_column(
|
||||
nullable=True, default=None
|
||||
)
|
||||
ProductODmm: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
ProductODin: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
TBXThreadAttribute: Mapped[str | None] = mapped_column(nullable=True, default=None)
|
||||
@@ -87,7 +87,6 @@ def _parse_thread_attribute(value: str) -> tuple[str, dict] | None:
|
||||
return "iso", {
|
||||
"diameter_mm": float(m.group("diameter")),
|
||||
"pitch_mm": float(m.group("pitch")),
|
||||
"extra": float(m.group("extra")),
|
||||
}
|
||||
|
||||
if m := _UNC_THREAD_RE.match(value):
|
||||
@@ -110,7 +109,9 @@ async def _get_or_create_attachment_point_name(db: AsyncSession, name: str):
|
||||
return apn
|
||||
|
||||
|
||||
async def _add_attachment_point(db: AsyncSession, ap: AugmentedPart, ip: ImportedPart) -> None:
|
||||
async def _add_attachment_point(
|
||||
db: AsyncSession, ap: AugmentedPart, ip: ImportedPart
|
||||
) -> None:
|
||||
"""Infer and create an attachment point for a newly-synced AugmentedPart.
|
||||
|
||||
Preference order: TBXThreadAttribute (ISO/UNC thread) then ProductODmm/in
|
||||
@@ -126,7 +127,9 @@ async def _add_attachment_point(db: AsyncSession, ap: AugmentedPart, ip: Importe
|
||||
|
||||
if kind == "iso":
|
||||
depth_mm = _parse_float(ip.ProductMaxThreadDepthmm)
|
||||
thread = await iso_thread_crud.get_by_values(db, depth_mm=depth_mm, **fields)
|
||||
thread = await iso_thread_crud.get_by_values(
|
||||
db, depth_mm=depth_mm, **fields
|
||||
)
|
||||
if thread is None:
|
||||
thread = await iso_thread_crud.create(db, depth_mm=depth_mm, **fields)
|
||||
await attachment_point_crud.create(
|
||||
@@ -137,7 +140,9 @@ async def _add_attachment_point(db: AsyncSession, ap: AugmentedPart, ip: Importe
|
||||
)
|
||||
elif kind == "unc":
|
||||
depth_in = _parse_float(ip.ProductMaxThreadDepthin)
|
||||
thread = await unc_thread_crud.get_by_values(db, depth_in=depth_in, **fields)
|
||||
thread = await unc_thread_crud.get_by_values(
|
||||
db, depth_in=depth_in, **fields
|
||||
)
|
||||
if thread is None:
|
||||
thread = await unc_thread_crud.create(db, depth_in=depth_in, **fields)
|
||||
await attachment_point_crud.create(
|
||||
@@ -146,6 +151,8 @@ async def _add_attachment_point(db: AsyncSession, ap: AugmentedPart, ip: Importe
|
||||
attachment_point_name_id=apn.id,
|
||||
unc_thread_id=thread.id,
|
||||
)
|
||||
else:
|
||||
pass
|
||||
return
|
||||
|
||||
od_mm = _parse_float(ip.ProductODmm)
|
||||
@@ -233,7 +240,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
||||
# ------------------------------------------------------------------ phase 1
|
||||
|
||||
# IDs referenced in existing associations
|
||||
result = await db.execute(select(augmented_part_imported_parts.c.imported_part_id).distinct())
|
||||
result = await db.execute(
|
||||
select(augmented_part_imported_parts.c.imported_part_id).distinct()
|
||||
)
|
||||
all_linked_ids: set[str] = {row[0] for row in result.all()}
|
||||
|
||||
# IDs that still exist in imported_parts
|
||||
@@ -247,7 +256,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
||||
# Which AugmentedParts are affected?
|
||||
result = await db.execute(
|
||||
select(augmented_part_imported_parts.c.augmented_part_id).where(
|
||||
augmented_part_imported_parts.c.imported_part_id.in_(orphaned_import_ids)
|
||||
augmented_part_imported_parts.c.imported_part_id.in_(
|
||||
orphaned_import_ids
|
||||
)
|
||||
)
|
||||
)
|
||||
orphaned_ap_ids: set[int] = {row[0] for row in result.all()}
|
||||
@@ -255,7 +266,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
||||
# Remove the stale association rows
|
||||
await db.execute(
|
||||
augmented_part_imported_parts.delete().where(
|
||||
augmented_part_imported_parts.c.imported_part_id.in_(orphaned_import_ids)
|
||||
augmented_part_imported_parts.c.imported_part_id.in_(
|
||||
orphaned_import_ids
|
||||
)
|
||||
)
|
||||
)
|
||||
await db.flush()
|
||||
@@ -278,7 +291,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
||||
# ------------------------------------------------------------------ phase 2
|
||||
|
||||
# Re-query linked IDs after phase 1 cleanup
|
||||
result = await db.execute(select(augmented_part_imported_parts.c.imported_part_id).distinct())
|
||||
result = await db.execute(
|
||||
select(augmented_part_imported_parts.c.imported_part_id).distinct()
|
||||
)
|
||||
now_linked_ids: set[str] = {row[0] for row in result.all()}
|
||||
unlinked_ids = current_ids - now_linked_ids
|
||||
|
||||
@@ -286,7 +301,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
||||
skipped = 0
|
||||
|
||||
if not unlinked_ids:
|
||||
return AugmentedPartSyncResult(created=created, deleted=deleted, skipped=skipped)
|
||||
return AugmentedPartSyncResult(
|
||||
created=created, deleted=deleted, skipped=skipped
|
||||
)
|
||||
|
||||
result = await db.execute(
|
||||
select(ImportedPart).where(ImportedPart.InventoryID.in_(unlinked_ids))
|
||||
|
||||
@@ -12,7 +12,7 @@ async def fetch(
|
||||
records: list[dict] = []
|
||||
next_url: str | None = url
|
||||
|
||||
headers= {"Accept": "application/json" }
|
||||
headers = {"Accept": "application/json"}
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
while next_url:
|
||||
response = await client.get(next_url, auth=auth, headers=headers)
|
||||
|
||||
+425
-119
File diff suppressed because it is too large.
Load diff
+25
-3
@@ -77,7 +77,22 @@ PERMISSIONS = [
|
||||
# Default roles -> permission names. "admin" gets everything.
|
||||
ROLES = {
|
||||
"admin": [name for name, _ in PERMISSIONS], # includes audit_log:read
|
||||
"user": ["users:read", "iso_threads:read", "unc_threads:read", "manufacturers:read", "scope_models:read", "part_colours:read", "part_types:read", "sub_part_types:read", "imported_parts:read", "static_connections:read", "clamp_connections:read", "attachment_point_names:read", "attachment_points:read", "augmented_parts:read"],
|
||||
"user": [
|
||||
"users:read",
|
||||
"iso_threads:read",
|
||||
"unc_threads:read",
|
||||
"manufacturers:read",
|
||||
"scope_models:read",
|
||||
"part_colours:read",
|
||||
"part_types:read",
|
||||
"sub_part_types:read",
|
||||
"imported_parts:read",
|
||||
"static_connections:read",
|
||||
"clamp_connections:read",
|
||||
"attachment_point_names:read",
|
||||
"attachment_points:read",
|
||||
"augmented_parts:read",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -123,8 +138,15 @@ async def seed() -> None:
|
||||
pt = await part_type_crud.get_by_name(db, pt_name)
|
||||
if pt is not None:
|
||||
for spt_name in spt_names:
|
||||
if await sub_part_type_crud.get_by_name_and_part_type(db, spt_name, pt.id) is None:
|
||||
await sub_part_type_crud.create(db, name=spt_name, part_type_id=pt.id)
|
||||
if (
|
||||
await sub_part_type_crud.get_by_name_and_part_type(
|
||||
db, spt_name, pt.id
|
||||
)
|
||||
is None
|
||||
):
|
||||
await sub_part_type_crud.create(
|
||||
db, name=spt_name, part_type_id=pt.id
|
||||
)
|
||||
|
||||
# Superuser
|
||||
existing = await user_crud.get_by_email(db, settings.first_superuser_email)
|
||||
|
||||
Reference in new issue
Block a user