Adding support for clamp inference
This commit is contained in:
1 parent
ab5315b862
commit
85cb58ef65
2 files changed
+59
-16
No files matched your search
@@ -38,6 +38,7 @@ from app.crud import manufacturer as manufacturer_crud
|
||||
from app.crud import part_colour as part_colour_crud
|
||||
from app.crud import part_type as part_type_crud
|
||||
from app.crud import static_connection as static_connection_crud
|
||||
from app.crud import clamp_connection as clamp_connection_crud
|
||||
from app.crud import unc_thread as unc_thread_crud
|
||||
from app.models.associations import augmented_part_imported_parts
|
||||
from app.models.augmented_part import AugmentedPart
|
||||
@@ -70,6 +71,11 @@ _UNC_THREAD_RE = re.compile(
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
_CLAMP_RANGE_RE = re.compile(
|
||||
r"\((?P<min_diameter>\d*\.?\d+)\s*-\s*(?P<max_diameter>\d*\.?\d+)\s*mm\)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def _parse_float(value: str | None) -> float | None:
|
||||
if value is None:
|
||||
@@ -101,6 +107,13 @@ def _parse_thread_attribute(value: str) -> tuple[str, dict] | None:
|
||||
"series": series,
|
||||
}
|
||||
|
||||
if m := _CLAMP_RANGE_RE.match(value):
|
||||
# Source data often omits the series letters; UNS ("unified special") is the
|
||||
# correct designation when the diameter/TPI pairing isn't a standard pitch.
|
||||
return "clamp", {
|
||||
"min_diametere": float(m.group("min_diameter")),
|
||||
"max_diameter": float(m.group("max_diameter")),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
@@ -111,7 +124,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
|
||||
@@ -131,7 +146,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(
|
||||
@@ -140,9 +157,12 @@ async def _add_attachment_point(db: AsyncSession, ap: AugmentedPart, ip: Importe
|
||||
attachment_point_name_id=apn.id,
|
||||
iso_thread_id=thread.id,
|
||||
)
|
||||
return
|
||||
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(
|
||||
@@ -151,9 +171,21 @@ 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
|
||||
elif kind == "clamp":
|
||||
clamp = await clamp_connection_crud.get_by_values(db, **fields)
|
||||
if clamp is None:
|
||||
clamp = await clamp_connection_crud.create(db, **fields)
|
||||
await attachment_point_crud.create(
|
||||
db,
|
||||
augmented_part_id=ap.id,
|
||||
attachment_point_name_id=apn.id,
|
||||
clamp_connection_id=clamp.id,
|
||||
)
|
||||
return
|
||||
else:
|
||||
# Unable to determine
|
||||
pass
|
||||
|
||||
od_mm = _parse_float(ip.ProductODmm)
|
||||
od_in = _parse_float(ip.ProductODin)
|
||||
@@ -240,7 +272,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
|
||||
@@ -254,7 +288,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()}
|
||||
@@ -262,7 +298,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()
|
||||
@@ -285,7 +323,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
|
||||
|
||||
@@ -293,7 +333,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))
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
- [ ] Add an `archived` attribute that indicates the part should not show up in default searches
|
||||
- [ ] Admins and users with permissions can opt into seeing archived parts
|
||||
- [ ] Add bulk "mark archived"
|
||||
- [ ] AugmentedPart Details
|
||||
- [x] Title should be the SKU
|
||||
- [ ] Add new inference for attachment point "Clamp"
|
||||
- [ ] Appears as `(44.5-47.5mm)` in the description
|
||||
- [ ] Scope Import
|
||||
- [ ] Test scopes with explicit fits populated
|
||||
- [ ] Attachment Points
|
||||
- [ ] Remove objective / occular labels and matching logic
|
||||
|
||||
- [ ] Dashboard
|
||||
- [ ] Make this more useful.
|
||||
- [ ] Number of
|
||||
@@ -17,6 +12,10 @@
|
||||
- [ ] Skipped parts from last Sync
|
||||
- [ ] Handy buttons to run the import / sync / recompute actions
|
||||
|
||||
- [x] AugmentedPart Details
|
||||
- [x] Title should be the SKU
|
||||
- [x] Add new inference for attachment point "Clamp"
|
||||
- [x] Appears as `(44.5-47.5mm)` in the description
|
||||
|
||||
- [x] Better layout for Scopes page
|
||||
- [x] Layout is too wide, requiring side-scrolling
|
||||
@@ -28,5 +27,7 @@
|
||||
- [x] Part Import
|
||||
- [x] Example query of `ZC5000` should return multiple parts
|
||||
- [x] Filter text should search SKU and description
|
||||
- [x] Attachment Points
|
||||
- [x] Remove objective / occular labels and matching logic
|
||||
|
||||
|
||||
Reference in new issue
Block a user