diff --git a/alembic/versions/0c3d1a5cff1f_add_part_colours.py b/alembic/versions/0c3d1a5cff1f_add_part_colours.py index 1a6896c..1cd2503 100644 --- a/alembic/versions/0c3d1a5cff1f_add_part_colours.py +++ b/alembic/versions/0c3d1a5cff1f_add_part_colours.py @@ -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 diff --git a/alembic/versions/5b6a7817b408_add_static_connections.py b/alembic/versions/5b6a7817b408_add_static_connections.py index 505f4a1..2ef3379 100644 --- a/alembic/versions/5b6a7817b408_add_static_connections.py +++ b/alembic/versions/5b6a7817b408_add_static_connections.py @@ -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 diff --git a/alembic/versions/70e48bda8829_add_attachment_point_names.py b/alembic/versions/70e48bda8829_add_attachment_point_names.py index d96ab49..2334a4a 100644 --- a/alembic/versions/70e48bda8829_add_attachment_point_names.py +++ b/alembic/versions/70e48bda8829_add_attachment_point_names.py @@ -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 diff --git a/alembic/versions/7be5ef599d3f_add_imported_parts_tables.py b/alembic/versions/7be5ef599d3f_add_imported_parts_tables.py index 402fe83..0a80d93 100644 --- a/alembic/versions/7be5ef599d3f_add_imported_parts_tables.py +++ b/alembic/versions/7be5ef599d3f_add_imported_parts_tables.py @@ -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 diff --git a/alembic/versions/8bc411b5814e_add_augmented_parts.py b/alembic/versions/8bc411b5814e_add_augmented_parts.py index 949891b..91a8c4c 100644 --- a/alembic/versions/8bc411b5814e_add_augmented_parts.py +++ b/alembic/versions/8bc411b5814e_add_augmented_parts.py @@ -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 diff --git a/alembic/versions/a192abbf4091_add_manufacturers_table.py b/alembic/versions/a192abbf4091_add_manufacturers_table.py index 1529003..f4687e0 100644 --- a/alembic/versions/a192abbf4091_add_manufacturers_table.py +++ b/alembic/versions/a192abbf4091_add_manufacturers_table.py @@ -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', - existing_type=sa.VARCHAR(length=8), - type_=sa.Enum('UNC', 'UNF', 'UNEF', 'UN', 'UNS', name='uncseries'), - existing_nullable=False) + 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, + ) # ### 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'), - type_=sa.VARCHAR(length=8), - existing_nullable=False) + 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, + ) - op.drop_table('manufacturers') + op.drop_table("manufacturers") # ### end Alembic commands ### diff --git a/alembic/versions/a52e7ca7a925_add_scope_models_table.py b/alembic/versions/a52e7ca7a925_add_scope_models_table.py index 2429716..984a273 100644 --- a/alembic/versions/a52e7ca7a925_add_scope_models_table.py +++ b/alembic/versions/a52e7ca7a925_add_scope_models_table.py @@ -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 ### diff --git a/alembic/versions/adf4a5b9493e_add_attachment_points.py b/alembic/versions/adf4a5b9493e_add_attachment_points.py index 9a428a8..e705b30 100644 --- a/alembic/versions/adf4a5b9493e_add_attachment_points.py +++ b/alembic/versions/adf4a5b9493e_add_attachment_points.py @@ -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 diff --git a/alembic/versions/b60d0aa3ceda_add_sub_part_types_table.py b/alembic/versions/b60d0aa3ceda_add_sub_part_types_table.py index bdca259..21db083 100644 --- a/alembic/versions/b60d0aa3ceda_add_sub_part_types_table.py +++ b/alembic/versions/b60d0aa3ceda_add_sub_part_types_table.py @@ -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 diff --git a/alembic/versions/bfbce324cd5f_add_augmented_part_imported_parts.py b/alembic/versions/bfbce324cd5f_add_augmented_part_imported_parts.py index 85dabea..84516e0 100644 --- a/alembic/versions/bfbce324cd5f_add_augmented_part_imported_parts.py +++ b/alembic/versions/bfbce324cd5f_add_augmented_part_imported_parts.py @@ -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 diff --git a/alembic/versions/c7882cedc9ae_add_clamp_connections.py b/alembic/versions/c7882cedc9ae_add_clamp_connections.py index 5675c60..8e542b8 100644 --- a/alembic/versions/c7882cedc9ae_add_clamp_connections.py +++ b/alembic/versions/c7882cedc9ae_add_clamp_connections.py @@ -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 diff --git a/alembic/versions/e23aa8dd2536_add_part_types_table.py b/alembic/versions/e23aa8dd2536_add_part_types_table.py index 3dfe743..dfe9198 100644 --- a/alembic/versions/e23aa8dd2536_add_part_types_table.py +++ b/alembic/versions/e23aa8dd2536_add_part_types_table.py @@ -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 ### diff --git a/alembic/versions/fd5379986da4_add_audit_logs_table.py b/alembic/versions/fd5379986da4_add_audit_logs_table.py index b891ef2..21cd6a5 100644 --- a/alembic/versions/fd5379986da4_add_audit_logs_table.py +++ b/alembic/versions/fd5379986da4_add_audit_logs_table.py @@ -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 diff --git a/app/api/attachment_point_names.py b/app/api/attachment_point_names.py index 26fc5bd..c8256e8 100644 --- a/app/api/attachment_point_names.py +++ b/app/api/attachment_point_names.py @@ -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), ) diff --git a/app/api/attachment_points.py b/app/api/attachment_points.py index 9d05978..2a44283 100644 --- a/app/api/attachment_points.py +++ b/app/api/attachment_points.py @@ -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 + ), ) diff --git a/app/api/augmented_parts.py b/app/api/augmented_parts.py index 32951bc..ac8d130 100644 --- a/app/api/augmented_parts.py +++ b/app/api/augmented_parts.py @@ -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 + ), ) diff --git a/app/api/clamp_connections.py b/app/api/clamp_connections.py index a1982f2..7a109ea 100644 --- a/app/api/clamp_connections.py +++ b/app/api/clamp_connections.py @@ -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 + ), ) diff --git a/app/api/imported_parts.py b/app/api/imported_parts.py index a02fb88..d91ddcb 100644 --- a/app/api/imported_parts.py +++ b/app/api/imported_parts.py @@ -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), ) diff --git a/app/api/iso_threads.py b/app/api/iso_threads.py index acc5b12..450ef74 100644 --- a/app/api/iso_threads.py +++ b/app/api/iso_threads.py @@ -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), ) diff --git a/app/api/manufacturers.py b/app/api/manufacturers.py index 0013a3e..074fd61 100644 --- a/app/api/manufacturers.py +++ b/app/api/manufacturers.py @@ -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), ) diff --git a/app/api/part_colours.py b/app/api/part_colours.py index 569d347..4237f06 100644 --- a/app/api/part_colours.py +++ b/app/api/part_colours.py @@ -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), ) diff --git a/app/api/part_types.py b/app/api/part_types.py index 2e41e92..e9565f1 100644 --- a/app/api/part_types.py +++ b/app/api/part_types.py @@ -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), ) diff --git a/app/api/roles.py b/app/api/roles.py index b37be45..3e96a5a 100644 --- a/app/api/roles.py +++ b/app/api/roles.py @@ -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), ) diff --git a/app/api/router.py b/app/api/router.py index 3eb265a..61fa1d4 100644 --- a/app/api/router.py +++ b/app/api/router.py @@ -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"]) diff --git a/app/api/scope_models.py b/app/api/scope_models.py index 239953c..963cbc6 100644 --- a/app/api/scope_models.py +++ b/app/api/scope_models.py @@ -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), ) diff --git a/app/api/static_connections.py b/app/api/static_connections.py index 4323c0b..bf1e288 100644 --- a/app/api/static_connections.py +++ b/app/api/static_connections.py @@ -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" diff --git a/app/api/sub_part_types.py b/app/api/sub_part_types.py index e37638e..6c90448 100644 --- a/app/api/sub_part_types.py +++ b/app/api/sub_part_types.py @@ -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), ) diff --git a/app/api/unc_threads.py b/app/api/unc_threads.py index 7b52203..9727f00 100644 --- a/app/api/unc_threads.py +++ b/app/api/unc_threads.py @@ -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), ) diff --git a/app/api/users.py b/app/api/users.py index 1b48b71..432ac54 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -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), ) diff --git a/app/crud/attachment_point.py b/app/crud/attachment_point.py index 948ba84..f58594d 100644 --- a/app/crud/attachment_point.py +++ b/app/crud/attachment_point.py @@ -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() diff --git a/app/crud/augmented_part.py b/app/crud/augmented_part.py index 0c54c81..9fee5f7 100644 --- a/app/crud/augmented_part.py +++ b/app/crud/augmented_part.py @@ -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( - ImportedPart.InventoryID.not_in( - select(augmented_part_imported_parts.c.imported_part_id) - ) + 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() diff --git a/app/crud/clamp_connection.py b/app/crud/clamp_connection.py index f6e3782..5da6784 100644 --- a/app/crud/clamp_connection.py +++ b/app/crud/clamp_connection.py @@ -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() diff --git a/app/crud/imported_part.py b/app/crud/imported_part.py index b21f59b..bd6bbaa 100644 --- a/app/crud/imported_part.py +++ b/app/crud/imported_part.py @@ -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()} diff --git a/app/crud/iso_thread.py b/app/crud/iso_thread.py index 2d5341d..9b3cc96 100644 --- a/app/crud/iso_thread.py +++ b/app/crud/iso_thread.py @@ -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) ) diff --git a/app/crud/manufacturer.py b/app/crud/manufacturer.py index 95f9d0e..118b382 100644 --- a/app/crud/manufacturer.py +++ b/app/crud/manufacturer.py @@ -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() diff --git a/app/crud/part_colour.py b/app/crud/part_colour.py index af0bd93..01db118 100644 --- a/app/crud/part_colour.py +++ b/app/crud/part_colour.py @@ -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() diff --git a/app/crud/part_type.py b/app/crud/part_type.py index 05c9cd3..fe03c70 100644 --- a/app/crud/part_type.py +++ b/app/crud/part_type.py @@ -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) ) diff --git a/app/crud/static_connection.py b/app/crud/static_connection.py index b4db31b..5f4fcec 100644 --- a/app/crud/static_connection.py +++ b/app/crud/static_connection.py @@ -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()) diff --git a/app/crud/sub_part_type.py b/app/crud/sub_part_type.py index 5e7d183..28681cb 100644 --- a/app/crud/sub_part_type.py +++ b/app/crud/sub_part_type.py @@ -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() diff --git a/app/crud/unc_thread.py b/app/crud/unc_thread.py index 41bb55d..62ee698 100644 --- a/app/crud/unc_thread.py +++ b/app/crud/unc_thread.py @@ -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) ) diff --git a/app/models/__init__.py b/app/models/__init__.py index 1c9ff3f..e5d5c71 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -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 diff --git a/app/models/attachment_point.py b/app/models/attachment_point.py index f1b1197..9accc15 100644 --- a/app/models/attachment_point.py +++ b/app/models/attachment_point.py @@ -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") diff --git a/app/models/augmented_part.py b/app/models/augmented_part.py index d47c7b8..757fafd 100644 --- a/app/models/augmented_part.py +++ b/app/models/augmented_part.py @@ -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 diff --git a/app/models/imported_part.py b/app/models/imported_part.py index 79c96eb..97cd726 100644 --- a/app/models/imported_part.py +++ b/app/models/imported_part.py @@ -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) diff --git a/app/services/augmented_part_sync.py b/app/services/augmented_part_sync.py index 21d42f5..17338e6 100644 --- a/app/services/augmented_part_sync.py +++ b/app/services/augmented_part_sync.py @@ -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)) diff --git a/app/services/odata.py b/app/services/odata.py index 19e66ad..8df507d 100644 --- a/app/services/odata.py +++ b/app/services/odata.py @@ -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) diff --git a/app/web/admin.py b/app/web/admin.py index 9269662..ef9de53 100644 --- a/app/web/admin.py +++ b/app/web/admin.py @@ -141,7 +141,10 @@ async def create_user( role_ids=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), ) @@ -209,7 +212,10 @@ async def update_user( } 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), ) @@ -227,7 +233,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), ) @@ -246,7 +255,11 @@ async def roles_page( return templates.TemplateResponse( request, "admin/roles.html", - {"user": user, "roles": await role_crud.list_roles(db), **(await _role_perm_ctx(db))}, + { + "user": user, + "roles": await role_crud.list_roles(db), + **(await _role_perm_ctx(db)), + }, ) @@ -267,7 +280,10 @@ async def create_role( permission_ids=permission_ids, ) await audit_crud.log( - db, actor=actor, action="create", resource_type="Role", + db, + actor=actor, + action="create", + resource_type="Role", resource_id=created.id, description=audit_crud.describe_create("Role", created.id, created.name), ) @@ -326,7 +342,10 @@ async def update_role( changes = {"permission_ids": permission_ids} 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), ) @@ -348,7 +367,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), ) @@ -387,9 +409,14 @@ async def create_iso_thread( depth_mm=float(depth_mm) if depth_mm.strip() else None, ) 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 templates.TemplateResponse( request, @@ -448,7 +475,10 @@ async def update_iso_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), ) @@ -468,7 +498,10 @@ async def delete_iso_thread( 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=thread_id, description=audit_crud.describe_delete("IsoThread", thread_id, name), ) @@ -513,9 +546,16 @@ async def create_unc_thread( depth_in=float(depth_in) if depth_in.strip() else None, ) 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 templates.TemplateResponse( request, @@ -578,7 +618,10 @@ async def update_unc_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), ) @@ -598,7 +641,10 @@ async def delete_unc_thread( 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=thread_id, description=audit_crud.describe_delete("UncThread", thread_id, name), ) @@ -637,9 +683,14 @@ async def create_manufacturer( logo_url=logo_url.strip() or None, ) await audit_crud.log( - db, actor=actor, action="create", resource_type="Manufacturer", + db, + actor=actor, + action="create", + resource_type="Manufacturer", resource_id=created.id, - description=audit_crud.describe_create("Manufacturer", created.id, created.name), + description=audit_crud.describe_create( + "Manufacturer", created.id, created.name + ), ) return templates.TemplateResponse( request, @@ -673,9 +724,7 @@ async def manufacturer_row( m = await manufacturer_crud.get(db, manufacturer_id) if m is None: raise HTTPException(status_code=404, detail="Manufacturer not found") - return templates.TemplateResponse( - request, "admin/_manufacturer_row.html", {"m": m} - ) + return templates.TemplateResponse(request, "admin/_manufacturer_row.html", {"m": m}) @router.post("/manufacturers/{manufacturer_id}", response_class=HTMLResponse) @@ -694,13 +743,16 @@ async def update_manufacturer( changes = {"name": name, "url": url, "logo_url": logo_url.strip() or None} await manufacturer_crud.update(db, m, changes) await audit_crud.log( - db, actor=actor, action="update", resource_type="Manufacturer", + db, + actor=actor, + action="update", + resource_type="Manufacturer", resource_id=manufacturer_id, - description=audit_crud.describe_update("Manufacturer", manufacturer_id, changes), - ) - return templates.TemplateResponse( - request, "admin/_manufacturer_row.html", {"m": m} + description=audit_crud.describe_update( + "Manufacturer", manufacturer_id, changes + ), ) + return templates.TemplateResponse(request, "admin/_manufacturer_row.html", {"m": m}) @router.delete("/manufacturers/{manufacturer_id}") @@ -714,9 +766,14 @@ async def delete_manufacturer( 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), + description=audit_crud.describe_delete( + "Manufacturer", manufacturer_id, name + ), ) return Response(status_code=status.HTTP_200_OK) @@ -757,7 +814,10 @@ async def create_scope_model( url=url.strip() or None, ) await audit_crud.log( - db, actor=actor, action="create", resource_type="ScopeModel", + db, + actor=actor, + action="create", + resource_type="ScopeModel", resource_id=created.id, description=audit_crud.describe_create("ScopeModel", created.id, created.name), ) @@ -813,10 +873,17 @@ async def update_scope_model( sm = await scope_model_crud.get(db, scope_model_id) if sm is None: raise HTTPException(status_code=404, detail="Scope model not found") - changes = {"name": name, "manufacturer_id": manufacturer_id, "url": url.strip() or None} + changes = { + "name": name, + "manufacturer_id": manufacturer_id, + "url": url.strip() or None, + } await scope_model_crud.update(db, sm, changes) await audit_crud.log( - db, actor=actor, action="update", resource_type="ScopeModel", + db, + actor=actor, + action="update", + resource_type="ScopeModel", resource_id=scope_model_id, description=audit_crud.describe_update("ScopeModel", scope_model_id, changes), ) @@ -836,7 +903,10 @@ async def delete_scope_model( 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), ) @@ -869,9 +939,14 @@ async def create_part_type( if not await part_type_crud.get_by_name(db, name): created = await part_type_crud.create(db, name=name) await audit_crud.log( - db, actor=actor, action="create", resource_type="PartType", + db, + actor=actor, + action="create", + resource_type="PartType", resource_id=created.id, - description=audit_crud.describe_create("PartType", created.id, created.name), + description=audit_crud.describe_create( + "PartType", created.id, created.name + ), ) return templates.TemplateResponse( request, @@ -905,9 +980,7 @@ async def part_type_row( pt = await part_type_crud.get(db, part_type_id) if pt is None: raise HTTPException(status_code=404, detail="Part type not found") - return templates.TemplateResponse( - request, "admin/_part_type_row.html", {"pt": pt} - ) + return templates.TemplateResponse(request, "admin/_part_type_row.html", {"pt": pt}) @router.post("/part-types/{part_type_id}", response_class=HTMLResponse) @@ -924,13 +997,14 @@ async def update_part_type( changes = {"name": name} 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=part_type_id, description=audit_crud.describe_update("PartType", part_type_id, changes), ) - return templates.TemplateResponse( - request, "admin/_part_type_row.html", {"pt": pt} - ) + return templates.TemplateResponse(request, "admin/_part_type_row.html", {"pt": pt}) @router.delete("/part-types/{part_type_id}") @@ -944,7 +1018,10 @@ async def delete_part_type( 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), ) @@ -980,11 +1057,18 @@ async def create_sub_part_type( actor: User = Depends(web_require("sub_part_types:write")), ): if not await sub_part_type_crud.get_by_name_and_part_type(db, name, part_type_id): - created = await sub_part_type_crud.create(db, name=name, part_type_id=part_type_id) + created = await sub_part_type_crud.create( + db, name=name, part_type_id=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=created.id, - description=audit_crud.describe_create("SubPartType", created.id, created.name), + description=audit_crud.describe_create( + "SubPartType", created.id, created.name + ), ) return templates.TemplateResponse( request, @@ -1040,9 +1124,14 @@ async def update_sub_part_type( changes = {"name": name, "part_type_id": part_type_id} 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=sub_part_type_id, - description=audit_crud.describe_update("SubPartType", sub_part_type_id, changes), + description=audit_crud.describe_update( + "SubPartType", sub_part_type_id, changes + ), ) return templates.TemplateResponse( request, "admin/_sub_part_type_row.html", {"spt": spt} @@ -1060,9 +1149,14 @@ async def delete_sub_part_type( 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), + description=audit_crud.describe_delete( + "SubPartType", sub_part_type_id, name + ), ) return Response(status_code=status.HTTP_200_OK) @@ -1084,7 +1178,14 @@ async def imported_parts_page( } total = await imported_part_crud.count(db) parts = await imported_part_crud.list_filtered(db, limit=_PAGE_SIZE) - pagination = _paginate("/admin/imported-parts/filter", {}, 1, _PAGE_SIZE, total, "#imported-parts-table-card") + pagination = _paginate( + "/admin/imported-parts/filter", + {}, + 1, + _PAGE_SIZE, + total, + "#imported-parts-table-card", + ) return templates.TemplateResponse( request, "admin/imported_parts.html", @@ -1111,7 +1212,10 @@ async def save_imported_part_config( password=kept_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", ) @@ -1129,29 +1233,51 @@ async def refresh_imported_parts( async def _imported_table_ctx(sync_result=None, sync_error=None): total = await imported_part_crud.count(db) parts = await imported_part_crud.list_filtered(db, limit=_PAGE_SIZE) - pagination = _paginate("/admin/imported-parts/filter", {}, 1, _PAGE_SIZE, total, "#imported-parts-table-card") - return {"parts": parts, "pagination": pagination, "sync_result": sync_result, "sync_error": sync_error} + pagination = _paginate( + "/admin/imported-parts/filter", + {}, + 1, + _PAGE_SIZE, + total, + "#imported-parts-table-card", + ) + return { + "parts": parts, + "pagination": pagination, + "sync_result": sync_result, + "sync_error": sync_error, + } if not config or not config.odata_url: return templates.TemplateResponse( - request, "admin/_imported_parts_table.html", + request, + "admin/_imported_parts_table.html", await _imported_table_ctx(sync_error="OData URL is not configured."), ) try: - 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)", ) return templates.TemplateResponse( - request, "admin/_imported_parts_table.html", - await _imported_table_ctx(sync_result=f"Sync complete: {created} created, {updated} updated."), + request, + "admin/_imported_parts_table.html", + await _imported_table_ctx( + sync_result=f"Sync complete: {created} created, {updated} updated." + ), ) except Exception as exc: return templates.TemplateResponse( - request, "admin/_imported_parts_table.html", + request, + "admin/_imported_parts_table.html", await _imported_table_ctx(sync_error=f"Sync failed: {exc}"), ) @@ -1169,11 +1295,16 @@ async def filter_imported_parts( pagination = _paginate( "/admin/imported-parts/filter", {"description": description}, - page, page_size, total, + page, + page_size, + total, "#imported-parts-table-card", ) parts = await imported_part_crud.list_filtered( - db, description=description, skip=pagination["skip"], limit=page_size, + db, + description=description, + skip=pagination["skip"], + limit=page_size, ) return templates.TemplateResponse( request, @@ -1207,8 +1338,17 @@ async def augmented_parts_page( ): ctx = await _augmented_part_form_ctx(db) total = await augmented_part_crud.count_filtered(db) - ctx["augmented_parts"] = await augmented_part_crud.list_filtered(db, limit=_PAGE_SIZE) - ctx["pagination"] = _paginate("/admin/augmented-parts/filter", {}, 1, _PAGE_SIZE, total, "#augmented-parts-table-card") + ctx["augmented_parts"] = await augmented_part_crud.list_filtered( + db, limit=_PAGE_SIZE + ) + ctx["pagination"] = _paginate( + "/admin/augmented-parts/filter", + {}, + 1, + _PAGE_SIZE, + total, + "#augmented-parts-table-card", + ) ctx["user"] = user return templates.TemplateResponse(request, "admin/augmented_parts.html", ctx) @@ -1237,7 +1377,10 @@ async def create_augmented_part( sub_part_type_id=int(sub_part_type_id) if sub_part_type_id else None, ) await audit_crud.log( - db, actor=actor, action="create", resource_type="AugmentedPart", + db, + actor=actor, + action="create", + resource_type="AugmentedPart", resource_id=created.id, description=audit_crud.describe_create( "AugmentedPart", created.id, created.sku or created.description @@ -1245,11 +1388,24 @@ async def create_augmented_part( ) form_ctx = await _augmented_part_form_ctx(db) total = await augmented_part_crud.count_filtered(db) - pagination = _paginate("/admin/augmented-parts/filter", {}, 1, _PAGE_SIZE, total, "#augmented-parts-table-card") + pagination = _paginate( + "/admin/augmented-parts/filter", + {}, + 1, + _PAGE_SIZE, + total, + "#augmented-parts-table-card", + ) return templates.TemplateResponse( request, "admin/_augmented_parts_table.html", - {**form_ctx, "augmented_parts": await augmented_part_crud.list_filtered(db, limit=_PAGE_SIZE), "pagination": pagination}, + { + **form_ctx, + "augmented_parts": await augmented_part_crud.list_filtered( + db, limit=_PAGE_SIZE + ), + "pagination": pagination, + }, ) @@ -1262,7 +1418,10 @@ async def sync_augmented_parts( try: result = await augmented_part_sync_service.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, " @@ -1271,7 +1430,11 @@ async def sync_augmented_parts( ) sync_result = ( f"Sync complete: {result.created} created, {result.deleted} deleted" - + (f", {result.skipped} skipped (type not recognised)" if result.skipped else "") + + ( + f", {result.skipped} skipped (type not recognised)" + if result.skipped + else "" + ) + "." ) sync_error = None @@ -1280,13 +1443,22 @@ async def sync_augmented_parts( sync_error = f"Sync failed: {exc}" form_ctx = await _augmented_part_form_ctx(db) total = await augmented_part_crud.count_filtered(db) - pagination = _paginate("/admin/augmented-parts/filter", {}, 1, _PAGE_SIZE, total, "#augmented-parts-table-card") + pagination = _paginate( + "/admin/augmented-parts/filter", + {}, + 1, + _PAGE_SIZE, + total, + "#augmented-parts-table-card", + ) return templates.TemplateResponse( request, "admin/_augmented_parts_table.html", { **form_ctx, - "augmented_parts": await augmented_part_crud.list_filtered(db, limit=_PAGE_SIZE), + "augmented_parts": await augmented_part_crud.list_filtered( + db, limit=_PAGE_SIZE + ), "pagination": pagination, "sync_result": sync_result, "sync_error": sync_error, @@ -1330,7 +1502,14 @@ async def filter_augmented_parts( "part_type_id": part_type_id, "sub_part_type_id": sub_part_type_id, } - pagination = _paginate("/admin/augmented-parts/filter", filter_params, page, page_size, total, "#augmented-parts-table-card") + pagination = _paginate( + "/admin/augmented-parts/filter", + filter_params, + page, + page_size, + total, + "#augmented-parts-table-card", + ) parts = await augmented_part_crud.list_filtered( db, description=description, @@ -1358,8 +1537,17 @@ async def skipped_parts_page( form_ctx = await _augmented_part_form_ctx(db) total_unlinked = await augmented_part_crud.count_unlinked_filtered(db) skipped_colours = await _skipped_colours(db) - pagination = _paginate("/admin/augmented-parts/skipped/filter", {}, 1, _PAGE_SIZE, total_unlinked, "#skipped-parts-table-card") - unlinked = await augmented_part_crud.get_unlinked_imported_parts_filtered(db, limit=_PAGE_SIZE) + pagination = _paginate( + "/admin/augmented-parts/skipped/filter", + {}, + 1, + _PAGE_SIZE, + total_unlinked, + "#skipped-parts-table-card", + ) + unlinked = await augmented_part_crud.get_unlinked_imported_parts_filtered( + db, limit=_PAGE_SIZE + ) return templates.TemplateResponse( request, "admin/augmented_parts_skipped.html", @@ -1409,7 +1597,10 @@ async def sync_skipped_parts( sub_part_type_id=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.description @@ -1426,8 +1617,17 @@ async def sync_skipped_parts( form_ctx = await _augmented_part_form_ctx(db) total_unlinked = await augmented_part_crud.count_unlinked_filtered(db) colours = await _skipped_colours(db) - pagination = _paginate("/admin/augmented-parts/skipped/filter", {}, 1, _PAGE_SIZE, total_unlinked, "#skipped-parts-table-card") - unlinked = await augmented_part_crud.get_unlinked_imported_parts_filtered(db, limit=_PAGE_SIZE) + pagination = _paginate( + "/admin/augmented-parts/skipped/filter", + {}, + 1, + _PAGE_SIZE, + total_unlinked, + "#skipped-parts-table-card", + ) + unlinked = await augmented_part_crud.get_unlinked_imported_parts_filtered( + db, limit=_PAGE_SIZE + ) return templates.TemplateResponse( request, "admin/_skipped_parts_table.html", @@ -1455,11 +1655,24 @@ async def filter_skipped_parts( db: AsyncSession = Depends(get_db), _: User = Depends(web_require("augmented_parts:read")), ): - total = await augmented_part_crud.count_unlinked_filtered(db, description=description, colour=colour) + total = await augmented_part_crud.count_unlinked_filtered( + db, description=description, colour=colour + ) filter_params = {"description": description, "colour": colour} - pagination = _paginate("/admin/augmented-parts/skipped/filter", filter_params, page, page_size, total, "#skipped-parts-table-card") + pagination = _paginate( + "/admin/augmented-parts/skipped/filter", + filter_params, + page, + page_size, + total, + "#skipped-parts-table-card", + ) unlinked = await augmented_part_crud.get_unlinked_imported_parts_filtered( - db, description=description, colour=colour, skip=pagination["skip"], limit=page_size, + db, + description=description, + colour=colour, + skip=pagination["skip"], + limit=page_size, ) return templates.TemplateResponse( request, @@ -1480,7 +1693,9 @@ async def edit_augmented_part_row( raise HTTPException(status_code=404, detail="Augmented part not found") ctx = await _augmented_part_form_ctx(db) ctx["ap"] = ap - return templates.TemplateResponse(request, "admin/_augmented_part_row_edit.html", ctx) + return templates.TemplateResponse( + request, "admin/_augmented_part_row_edit.html", ctx + ) @router.get("/augmented-parts/{augmented_part_id}/row", response_class=HTMLResponse) @@ -1526,9 +1741,14 @@ async def update_augmented_part( } 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=augmented_part_id, - description=audit_crud.describe_update("AugmentedPart", augmented_part_id, changes), + description=audit_crud.describe_update( + "AugmentedPart", augmented_part_id, changes + ), ) return templates.TemplateResponse( request, "admin/_augmented_part_row.html", {"ap": ap} @@ -1546,9 +1766,14 @@ 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 + ), ) return Response(status_code=status.HTTP_200_OK) @@ -1568,7 +1793,8 @@ async def _augmented_part_ap_ctx(db: AsyncSession, augmented_part_id: int) -> di @router.get( - "/augmented-parts/{augmented_part_id}/attachment-points", response_class=HTMLResponse + "/augmented-parts/{augmented_part_id}/attachment-points", + response_class=HTMLResponse, ) async def augmented_part_attachment_points( request: Request, @@ -1583,7 +1809,8 @@ async def augmented_part_attachment_points( @router.post( - "/augmented-parts/{augmented_part_id}/attachment-points", response_class=HTMLResponse + "/augmented-parts/{augmented_part_id}/attachment-points", + response_class=HTMLResponse, ) async def create_augmented_part_attachment_point( request: Request, @@ -1604,14 +1831,18 @@ async def create_augmented_part_attachment_point( iso_thread_id=int(iso_thread_id) if iso_thread_id else None, unc_thread_id=int(unc_thread_id) if unc_thread_id else None, clamp_connection_id=int(clamp_connection_id) if clamp_connection_id else None, - static_connection_id=int(static_connection_id) if static_connection_id else None, + static_connection_id=( + int(static_connection_id) if static_connection_id else None + ), ) await audit_crud.log( - db, actor=actor, action="create", resource_type="AttachmentPoint", + db, + actor=actor, + action="create", + resource_type="AttachmentPoint", resource_id=apoint.id, description=audit_crud.describe_create( - "AttachmentPoint", apoint.id, - f"part {augmented_part_id}" + "AttachmentPoint", apoint.id, f"part {augmented_part_id}" ), ) ctx = await _augmented_part_ap_ctx(db, augmented_part_id) @@ -1694,12 +1925,19 @@ async def update_augmented_part_attachment_point( "attachment_point_name_id": attachment_point_name_id, "iso_thread_id": int(iso_thread_id) if iso_thread_id else None, "unc_thread_id": int(unc_thread_id) if unc_thread_id else None, - "clamp_connection_id": int(clamp_connection_id) if clamp_connection_id else None, - "static_connection_id": int(static_connection_id) if static_connection_id else None, + "clamp_connection_id": ( + int(clamp_connection_id) if clamp_connection_id else None + ), + "static_connection_id": ( + int(static_connection_id) if static_connection_id else None + ), } apoint = await attachment_point_crud.update(db, apoint, changes) await audit_crud.log( - db, actor=actor, action="update", resource_type="AttachmentPoint", + db, + actor=actor, + action="update", + resource_type="AttachmentPoint", resource_id=attachment_point_id, description=audit_crud.describe_update( "AttachmentPoint", attachment_point_id, changes @@ -1725,7 +1963,10 @@ async def delete_augmented_part_attachment_point( if apoint is not None: await attachment_point_crud.delete(db, apoint) 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, f"part {augmented_part_id}" @@ -1763,9 +2004,14 @@ async def create_part_colour( if not await part_colour_crud.get_by_name(db, name): created = await part_colour_crud.create(db, name=name) await audit_crud.log( - db, actor=actor, action="create", resource_type="PartColour", + db, + actor=actor, + action="create", + resource_type="PartColour", resource_id=created.id, - description=audit_crud.describe_create("PartColour", created.id, created.name), + description=audit_crud.describe_create( + "PartColour", created.id, created.name + ), ) return templates.TemplateResponse( request, @@ -1818,7 +2064,10 @@ async def update_part_colour( changes = {"name": name} 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=part_colour_id, description=audit_crud.describe_update("PartColour", part_colour_id, changes), ) @@ -1838,7 +2087,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), ) @@ -1871,9 +2123,14 @@ async def create_attachment_point_name( if not await apn_crud.get_by_name(db, name): created = await apn_crud.create(db, name=name) await audit_crud.log( - db, actor=actor, action="create", resource_type="AttachmentPointName", + db, + actor=actor, + action="create", + resource_type="AttachmentPointName", resource_id=created.id, - description=audit_crud.describe_create("AttachmentPointName", created.id, name), + description=audit_crud.describe_create( + "AttachmentPointName", created.id, name + ), ) return templates.TemplateResponse( request, @@ -1926,7 +2183,10 @@ async def update_attachment_point_name( changes = {"name": name} 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), ) @@ -1946,7 +2206,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), ) @@ -2037,7 +2300,10 @@ async def create_attachment_point( static_connection_id=static_id, ) 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, _ap_connection_label(ap) @@ -2050,7 +2316,9 @@ async def create_attachment_point( ) -@router.get("/attachment-points/{attachment_point_id}/edit", response_class=HTMLResponse) +@router.get( + "/attachment-points/{attachment_point_id}/edit", response_class=HTMLResponse +) async def edit_attachment_point_row( request: Request, attachment_point_id: int, @@ -2114,12 +2382,19 @@ async def update_attachment_point( "attachment_point_name_id": attachment_point_name_id, "iso_thread_id": int(iso_thread_id) if iso_thread_id else None, "unc_thread_id": int(unc_thread_id) if unc_thread_id else None, - "clamp_connection_id": int(clamp_connection_id) if clamp_connection_id else None, - "static_connection_id": int(static_connection_id) if static_connection_id else None, + "clamp_connection_id": ( + int(clamp_connection_id) if clamp_connection_id else None + ), + "static_connection_id": ( + int(static_connection_id) if static_connection_id else None + ), } 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=attachment_point_id, description=audit_crud.describe_update( "AttachmentPoint", attachment_point_id, changes @@ -2141,7 +2416,10 @@ async def delete_attachment_point( label = _ap_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 @@ -2177,7 +2455,10 @@ async def create_static_connection( if not await static_connection_crud.get_by_od_mm(db, od_mm): created = await static_connection_crud.create(db, od_mm=od_mm, od_in=od_in) await audit_crud.log( - db, actor=actor, action="create", resource_type="StaticConnection", + db, + actor=actor, + action="create", + resource_type="StaticConnection", resource_id=created.id, description=audit_crud.describe_create( "StaticConnection", created.id, f"{created.od_mm} mm" @@ -2190,7 +2471,9 @@ async def create_static_connection( ) -@router.get("/static-connections/{static_connection_id}/edit", response_class=HTMLResponse) +@router.get( + "/static-connections/{static_connection_id}/edit", response_class=HTMLResponse +) async def edit_static_connection_row( request: Request, static_connection_id: int, @@ -2205,7 +2488,9 @@ async def edit_static_connection_row( ) -@router.get("/static-connections/{static_connection_id}/row", response_class=HTMLResponse) +@router.get( + "/static-connections/{static_connection_id}/row", response_class=HTMLResponse +) async def static_connection_row( request: Request, static_connection_id: int, @@ -2235,9 +2520,14 @@ async def update_static_connection( changes = {"od_mm": od_mm, "od_in": od_in} 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=static_connection_id, - description=audit_crud.describe_update("StaticConnection", static_connection_id, changes), + description=audit_crud.describe_update( + "StaticConnection", static_connection_id, changes + ), ) return templates.TemplateResponse( request, "admin/_static_connection_row.html", {"sc": sc} @@ -2255,7 +2545,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" @@ -2298,11 +2591,15 @@ async def create_clamp_connection( max_in=float(max_in) if max_in else None, ) 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 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, label), ) @@ -2313,7 +2610,9 @@ async def create_clamp_connection( ) -@router.get("/clamp-connections/{clamp_connection_id}/edit", response_class=HTMLResponse) +@router.get( + "/clamp-connections/{clamp_connection_id}/edit", response_class=HTMLResponse +) async def edit_clamp_connection_row( request: Request, clamp_connection_id: int, @@ -2365,7 +2664,10 @@ async def update_clamp_connection( } 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=clamp_connection_id, description=audit_crud.describe_update( "ClampConnection", clamp_connection_id, changes @@ -2385,12 +2687,16 @@ async def delete_clamp_connection( cc = await clamp_connection_crud.get(db, clamp_connection_id) if cc is not None: 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 diff --git a/scripts/seed.py b/scripts/seed.py index 72a2505..1b2d256 100644 --- a/scripts/seed.py +++ b/scripts/seed.py @@ -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)