Adding ability to autovivify an appropriate attachment point when manually entering a fit
This commit is contained in:
1 parent
92ca6b2a9f
commit
23d07505bd
3 files changed
+136
-6
No files matched your search
@@ -1,6 +1,13 @@
|
|||||||
|
{% if points %}
|
||||||
<select name="other_attachment_point_id" required>
|
<select name="other_attachment_point_id" required>
|
||||||
<option value="">— select —</option>
|
<option value="">— select —</option>
|
||||||
{% for p in points %}
|
{% for p in points %}
|
||||||
<option value="{{ p.id }}">{{ p.attachment_point_name.name }}</option>
|
<option value="{{ p.id }}">{{ p.attachment_point_name.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
{% else %}
|
||||||
|
<input type="hidden" name="other_attachment_point_id" value="">
|
||||||
|
<p class="muted" style="font-size:0.85rem;margin:0">
|
||||||
|
This part has no attachment points yet — one matching yours will be created automatically.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
+44
-6
@@ -2268,24 +2268,62 @@ async def create_augmented_part_fit(
|
|||||||
request: Request,
|
request: Request,
|
||||||
augmented_part_id: int,
|
augmented_part_id: int,
|
||||||
own_attachment_point_id: int = Form(...),
|
own_attachment_point_id: int = Form(...),
|
||||||
|
other_augmented_part_id: str = Form(""),
|
||||||
other_attachment_point_id: str = Form(""),
|
other_attachment_point_id: str = Form(""),
|
||||||
label: FitLabel = Form(FitLabel.EXACT),
|
label: FitLabel = Form(FitLabel.EXACT),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
actor: User = Depends(web_require("fits:write")),
|
actor: User = Depends(web_require("fits:write")),
|
||||||
):
|
):
|
||||||
error = None
|
error = None
|
||||||
if not other_attachment_point_id:
|
auto_created_ap = None
|
||||||
|
ap_a = await attachment_point_crud.get(db, own_attachment_point_id)
|
||||||
|
ap_b = None
|
||||||
|
if ap_a is None:
|
||||||
|
error = "Attachment point not found."
|
||||||
|
elif other_attachment_point_id:
|
||||||
|
ap_b = await attachment_point_crud.get(db, int(other_attachment_point_id))
|
||||||
|
if ap_b is None:
|
||||||
|
error = "Attachment point not found."
|
||||||
|
elif other_augmented_part_id:
|
||||||
|
other_part = await augmented_part_crud.get(db, int(other_augmented_part_id))
|
||||||
|
if other_part is None:
|
||||||
|
error = "Part not found."
|
||||||
|
elif await attachment_point_crud.list_for_augmented_part(db, other_part.id):
|
||||||
error = "Select the other part's attachment point."
|
error = "Select the other part's attachment point."
|
||||||
else:
|
else:
|
||||||
ap_a = await attachment_point_crud.get(db, own_attachment_point_id)
|
ap_b = await attachment_point_crud.create(
|
||||||
ap_b = await attachment_point_crud.get(db, int(other_attachment_point_id))
|
db,
|
||||||
if ap_a is None or ap_b is None:
|
augmented_part_id=other_part.id,
|
||||||
error = "Attachment point not found."
|
attachment_point_name_id=ap_a.attachment_point_name_id,
|
||||||
elif ap_a.augmented_part_id == ap_b.augmented_part_id:
|
iso_thread_id=ap_a.iso_thread_id,
|
||||||
|
unc_thread_id=ap_a.unc_thread_id,
|
||||||
|
clamp_connection_id=ap_a.clamp_connection_id,
|
||||||
|
static_connection_id=ap_a.static_connection_id,
|
||||||
|
)
|
||||||
|
auto_created_ap = ap_b
|
||||||
|
else:
|
||||||
|
error = "Select the other part's attachment point."
|
||||||
|
|
||||||
|
if error is None and ap_b is not None:
|
||||||
|
if ap_a.augmented_part_id == ap_b.augmented_part_id:
|
||||||
error = "A part cannot fit itself."
|
error = "A part cannot fit itself."
|
||||||
elif await fit_crud.get_by_attachment_points(db, ap_a, ap_b) is not None:
|
elif await fit_crud.get_by_attachment_points(db, ap_a, ap_b) is not None:
|
||||||
error = "That fit already exists."
|
error = "That fit already exists."
|
||||||
else:
|
else:
|
||||||
|
if auto_created_ap is not None:
|
||||||
|
await audit_crud.log(
|
||||||
|
db,
|
||||||
|
actor=actor,
|
||||||
|
action="create",
|
||||||
|
resource_type="AttachmentPoint",
|
||||||
|
resource_id=auto_created_ap.id,
|
||||||
|
description=audit_crud.describe_create(
|
||||||
|
"AttachmentPoint",
|
||||||
|
auto_created_ap.id,
|
||||||
|
f"auto-created on AugmentedPart #{auto_created_ap.augmented_part_id} "
|
||||||
|
f"to match attachment point {ap_a.id}",
|
||||||
|
),
|
||||||
|
)
|
||||||
fit = await fit_crud.create(db, ap_a=ap_a, ap_b=ap_b, label=label)
|
fit = await fit_crud.create(db, ap_a=ap_a, ap_b=ap_b, label=label)
|
||||||
await audit_crud.log(
|
await audit_crud.log(
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from app.models.attachment_point import AttachmentPoint
|
|||||||
from app.models.attachment_point_name import AttachmentPointName
|
from app.models.attachment_point_name import AttachmentPointName
|
||||||
from app.models.augmented_part import AugmentedPart
|
from app.models.augmented_part import AugmentedPart
|
||||||
from app.models.fit import Fit, FitLabel
|
from app.models.fit import Fit, FitLabel
|
||||||
|
from app.models.iso_thread import IsoThread
|
||||||
from app.models.part_type import PartType
|
from app.models.part_type import PartType
|
||||||
from app.models.scope_info import ScopeInfo
|
from app.models.scope_info import ScopeInfo
|
||||||
|
|
||||||
@@ -151,6 +152,90 @@ async def test_detail_write_controls_for_fit_editor(
|
|||||||
assert "Recompute possible fits" in body
|
assert "Recompute possible fits" in body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_create_fit_auto_creates_matching_attachment_point(
|
||||||
|
client, seeded, make_user, db_sessionmaker
|
||||||
|
):
|
||||||
|
async with db_sessionmaker() as db:
|
||||||
|
apn = AttachmentPointName(name="Objective")
|
||||||
|
scope_t = PartType(name="Scope")
|
||||||
|
cover_t = PartType(name="Flip Cover")
|
||||||
|
db.add_all([apn, scope_t, cover_t])
|
||||||
|
await db.flush()
|
||||||
|
|
||||||
|
thread = IsoThread(diameter_mm=42.0, pitch_mm=0.75)
|
||||||
|
db.add(thread)
|
||||||
|
await db.flush()
|
||||||
|
|
||||||
|
scope = AugmentedPart(
|
||||||
|
description="Alpha 3-18x50",
|
||||||
|
sku="ALP-318",
|
||||||
|
colour_id=1,
|
||||||
|
manufacturer_id=1,
|
||||||
|
part_type_id=scope_t.id,
|
||||||
|
)
|
||||||
|
cover = AugmentedPart(
|
||||||
|
description="Flip cover 50mm",
|
||||||
|
colour_id=1,
|
||||||
|
manufacturer_id=1,
|
||||||
|
part_type_id=cover_t.id,
|
||||||
|
)
|
||||||
|
db.add_all([scope, cover])
|
||||||
|
await db.flush()
|
||||||
|
|
||||||
|
own_ap = AttachmentPoint(
|
||||||
|
augmented_part_id=scope.id,
|
||||||
|
attachment_point_name_id=apn.id,
|
||||||
|
iso_thread_id=thread.id,
|
||||||
|
)
|
||||||
|
db.add(own_ap)
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
scope_id, cover_id, own_ap_id = scope.id, cover.id, own_ap.id
|
||||||
|
|
||||||
|
perms = seeded["perms"]
|
||||||
|
editor = await role_crud.create(
|
||||||
|
db,
|
||||||
|
name="fit-editor2",
|
||||||
|
permission_ids=[
|
||||||
|
perms["augmented_parts:read"].id,
|
||||||
|
perms["fits:read"].id,
|
||||||
|
perms["fits:write"].id,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
editor_id = editor.id
|
||||||
|
await make_user("editor2@example.com", role_ids=[editor_id])
|
||||||
|
await _web_login(client, "editor2@example.com")
|
||||||
|
|
||||||
|
resp = await client.post(
|
||||||
|
f"/admin/augmented-parts/{scope_id}/fits",
|
||||||
|
data={
|
||||||
|
"own_attachment_point_id": own_ap_id,
|
||||||
|
"other_augmented_part_id": cover_id,
|
||||||
|
"other_attachment_point_id": "",
|
||||||
|
"label": "possible",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.text
|
||||||
|
assert "already exists" not in body
|
||||||
|
assert "not found" not in body
|
||||||
|
|
||||||
|
async with db_sessionmaker() as db:
|
||||||
|
cover_points = [
|
||||||
|
ap
|
||||||
|
for ap in (await db.execute(AttachmentPoint.__table__.select())).all()
|
||||||
|
if ap.augmented_part_id == cover_id
|
||||||
|
]
|
||||||
|
assert len(cover_points) == 1
|
||||||
|
assert cover_points[0].iso_thread_id == thread.id
|
||||||
|
assert cover_points[0].attachment_point_name_id == apn.id
|
||||||
|
|
||||||
|
fits = (await db.execute(Fit.__table__.select())).all()
|
||||||
|
assert len(fits) == 1
|
||||||
|
assert fits[0].label == FitLabel.POSSIBLE
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_detail_404_for_missing_part(client, seeded, make_user):
|
async def test_detail_404_for_missing_part(client, seeded, make_user):
|
||||||
await make_user("reader2@example.com", role_ids=[seeded["roles"]["user"].id])
|
await make_user("reader2@example.com", role_ids=[seeded["roles"]["user"].id])
|
||||||
|
|||||||
Reference in new issue
Block a user