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_colour as part_colour_crud
|
||||||
from app.crud import part_type as part_type_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 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.crud import unc_thread as unc_thread_crud
|
||||||
from app.models.associations import augmented_part_imported_parts
|
from app.models.associations import augmented_part_imported_parts
|
||||||
from app.models.augmented_part import AugmentedPart
|
from app.models.augmented_part import AugmentedPart
|
||||||
@@ -70,6 +71,11 @@ _UNC_THREAD_RE = re.compile(
|
|||||||
re.IGNORECASE,
|
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:
|
def _parse_float(value: str | None) -> float | None:
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -101,6 +107,13 @@ def _parse_thread_attribute(value: str) -> tuple[str, dict] | None:
|
|||||||
"series": series,
|
"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
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -111,7 +124,9 @@ async def _get_or_create_attachment_point_name(db: AsyncSession, name: str):
|
|||||||
return apn
|
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.
|
"""Infer and create an attachment point for a newly-synced AugmentedPart.
|
||||||
|
|
||||||
Preference order: TBXThreadAttribute (ISO/UNC thread) then ProductODmm/in
|
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":
|
if kind == "iso":
|
||||||
depth_mm = _parse_float(ip.ProductMaxThreadDepthmm)
|
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:
|
if thread is None:
|
||||||
thread = await iso_thread_crud.create(db, depth_mm=depth_mm, **fields)
|
thread = await iso_thread_crud.create(db, depth_mm=depth_mm, **fields)
|
||||||
await attachment_point_crud.create(
|
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,
|
attachment_point_name_id=apn.id,
|
||||||
iso_thread_id=thread.id,
|
iso_thread_id=thread.id,
|
||||||
)
|
)
|
||||||
|
return
|
||||||
elif kind == "unc":
|
elif kind == "unc":
|
||||||
depth_in = _parse_float(ip.ProductMaxThreadDepthin)
|
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:
|
if thread is None:
|
||||||
thread = await unc_thread_crud.create(db, depth_in=depth_in, **fields)
|
thread = await unc_thread_crud.create(db, depth_in=depth_in, **fields)
|
||||||
await attachment_point_crud.create(
|
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,
|
attachment_point_name_id=apn.id,
|
||||||
unc_thread_id=thread.id,
|
unc_thread_id=thread.id,
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
pass
|
|
||||||
return
|
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_mm = _parse_float(ip.ProductODmm)
|
||||||
od_in = _parse_float(ip.ProductODin)
|
od_in = _parse_float(ip.ProductODin)
|
||||||
@@ -240,7 +272,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
|||||||
# ------------------------------------------------------------------ phase 1
|
# ------------------------------------------------------------------ phase 1
|
||||||
|
|
||||||
# IDs referenced in existing associations
|
# 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()}
|
all_linked_ids: set[str] = {row[0] for row in result.all()}
|
||||||
|
|
||||||
# IDs that still exist in imported_parts
|
# IDs that still exist in imported_parts
|
||||||
@@ -254,7 +288,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
|||||||
# Which AugmentedParts are affected?
|
# Which AugmentedParts are affected?
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(augmented_part_imported_parts.c.augmented_part_id).where(
|
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()}
|
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
|
# Remove the stale association rows
|
||||||
await db.execute(
|
await db.execute(
|
||||||
augmented_part_imported_parts.delete().where(
|
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()
|
await db.flush()
|
||||||
@@ -285,7 +323,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
|||||||
# ------------------------------------------------------------------ phase 2
|
# ------------------------------------------------------------------ phase 2
|
||||||
|
|
||||||
# Re-query linked IDs after phase 1 cleanup
|
# 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()}
|
now_linked_ids: set[str] = {row[0] for row in result.all()}
|
||||||
unlinked_ids = current_ids - now_linked_ids
|
unlinked_ids = current_ids - now_linked_ids
|
||||||
|
|
||||||
@@ -293,7 +333,9 @@ async def sync(db: AsyncSession) -> AugmentedPartSyncResult:
|
|||||||
skipped = 0
|
skipped = 0
|
||||||
|
|
||||||
if not unlinked_ids:
|
if not unlinked_ids:
|
||||||
return AugmentedPartSyncResult(created=created, deleted=deleted, skipped=skipped)
|
return AugmentedPartSyncResult(
|
||||||
|
created=created, deleted=deleted, skipped=skipped
|
||||||
|
)
|
||||||
|
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(ImportedPart).where(ImportedPart.InventoryID.in_(unlinked_ids))
|
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
|
- [ ] 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
|
- [ ] Admins and users with permissions can opt into seeing archived parts
|
||||||
- [ ] Add bulk "mark archived"
|
- [ ] 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
|
- [ ] Scope Import
|
||||||
- [ ] Test scopes with explicit fits populated
|
- [ ] Test scopes with explicit fits populated
|
||||||
- [ ] Attachment Points
|
|
||||||
- [ ] Remove objective / occular labels and matching logic
|
|
||||||
- [ ] Dashboard
|
- [ ] Dashboard
|
||||||
- [ ] Make this more useful.
|
- [ ] Make this more useful.
|
||||||
- [ ] Number of
|
- [ ] Number of
|
||||||
@@ -17,6 +12,10 @@
|
|||||||
- [ ] Skipped parts from last Sync
|
- [ ] Skipped parts from last Sync
|
||||||
- [ ] Handy buttons to run the import / sync / recompute actions
|
- [ ] 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] Better layout for Scopes page
|
||||||
- [x] Layout is too wide, requiring side-scrolling
|
- [x] Layout is too wide, requiring side-scrolling
|
||||||
@@ -28,5 +27,7 @@
|
|||||||
- [x] Part Import
|
- [x] Part Import
|
||||||
- [x] Example query of `ZC5000` should return multiple parts
|
- [x] Example query of `ZC5000` should return multiple parts
|
||||||
- [x] Filter text should search SKU and description
|
- [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