Adding fits
This commit is contained in:
1 parent
8185f1f8b3
commit
ab39191af5
19 files changed
+1253
-1
No files matched your search
@@ -21,14 +21,18 @@ from app.crud import scope_model as scope_model_crud
|
||||
from app.crud import attachment_point as attachment_point_crud
|
||||
from app.crud import attachment_point_name as apn_crud
|
||||
from app.crud import clamp_connection as clamp_connection_crud
|
||||
from app.crud import fit as fit_crud
|
||||
from app.crud import fit_note as fit_note_crud
|
||||
from app.crud import static_connection as static_connection_crud
|
||||
from app.crud import sub_part_type as sub_part_type_crud
|
||||
from app.crud import unc_thread as unc_thread_crud
|
||||
from app.crud import user as user_crud
|
||||
from app.deps import get_current_web_user
|
||||
from app.models.fit import FitLabel
|
||||
from app.models.unc_thread import UncSeries
|
||||
from app.models.user import User
|
||||
from app.services import augmented_part_sync as augmented_part_sync_service
|
||||
from app.services import fit_matching as fit_matching_service
|
||||
from app.services import odata as odata_service
|
||||
from app.web.templates import templates
|
||||
|
||||
@@ -1978,6 +1982,258 @@ async def delete_augmented_part_attachment_point(
|
||||
)
|
||||
|
||||
|
||||
# --- Fits ----------------------------------------------------------------
|
||||
|
||||
|
||||
async def _fit_view(db: AsyncSession, fit, part_id: int) -> dict:
|
||||
if fit.augmented_part_a_id == part_id:
|
||||
own_ap, other_ap, other_part_id = (
|
||||
fit.attachment_point_a,
|
||||
fit.attachment_point_b,
|
||||
fit.augmented_part_b_id,
|
||||
)
|
||||
else:
|
||||
own_ap, other_ap, other_part_id = (
|
||||
fit.attachment_point_b,
|
||||
fit.attachment_point_a,
|
||||
fit.augmented_part_a_id,
|
||||
)
|
||||
other_part = await augmented_part_crud.get(db, other_part_id)
|
||||
return {"fit": fit, "own_ap": own_ap, "other_ap": other_ap, "other_part": other_part}
|
||||
|
||||
|
||||
async def _augmented_part_fits_ctx(
|
||||
db: AsyncSession,
|
||||
augmented_part_id: int,
|
||||
*,
|
||||
message: str | None = None,
|
||||
error: str | None = None,
|
||||
) -> dict:
|
||||
fits = await fit_crud.list_for_part(db, augmented_part_id)
|
||||
other_parts = [
|
||||
p
|
||||
for p in await augmented_part_crud.list_all(db, limit=500)
|
||||
if p.id != augmented_part_id
|
||||
]
|
||||
return {
|
||||
"augmented_part_id": augmented_part_id,
|
||||
"fit_views": [await _fit_view(db, f, augmented_part_id) for f in fits],
|
||||
"own_points": await attachment_point_crud.list_for_augmented_part(
|
||||
db, augmented_part_id
|
||||
),
|
||||
"other_parts": other_parts,
|
||||
"message": message,
|
||||
"error": error,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/augmented-parts/{augmented_part_id}/fits", response_class=HTMLResponse)
|
||||
async def augmented_part_fits(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_: User = Depends(web_require("fits:read")),
|
||||
):
|
||||
ctx = await _augmented_part_fits_ctx(db, augmented_part_id)
|
||||
return templates.TemplateResponse(request, "admin/_augmented_part_fits.html", ctx)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/augmented-parts/{augmented_part_id}/fits/recompute", response_class=HTMLResponse
|
||||
)
|
||||
async def recompute_augmented_part_fits(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("fits:write")),
|
||||
):
|
||||
result = await fit_matching_service.recompute_for_part(db, augmented_part_id)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="Fit",
|
||||
resource_id=augmented_part_id,
|
||||
description=(
|
||||
f"Recomputed fits for AugmentedPart #{augmented_part_id}: "
|
||||
f"{result.created} created, {result.removed} removed"
|
||||
),
|
||||
)
|
||||
ctx = await _augmented_part_fits_ctx(
|
||||
db,
|
||||
augmented_part_id,
|
||||
message=f"{result.created} created, {result.removed} removed.",
|
||||
)
|
||||
return templates.TemplateResponse(request, "admin/_augmented_part_fits.html", ctx)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/augmented-parts/{augmented_part_id}/fits/other-points",
|
||||
response_class=HTMLResponse,
|
||||
)
|
||||
async def fit_other_points(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
other_augmented_part_id: str = "",
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_: User = Depends(web_require("fits:write")),
|
||||
):
|
||||
points = (
|
||||
await attachment_point_crud.list_for_augmented_part(
|
||||
db, int(other_augmented_part_id)
|
||||
)
|
||||
if other_augmented_part_id
|
||||
else []
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
request, "admin/_fit_other_points_select.html", {"points": points}
|
||||
)
|
||||
|
||||
|
||||
@router.post("/augmented-parts/{augmented_part_id}/fits", response_class=HTMLResponse)
|
||||
async def create_augmented_part_fit(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
own_attachment_point_id: int = Form(...),
|
||||
other_attachment_point_id: str = Form(""),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("fits:write")),
|
||||
):
|
||||
error = None
|
||||
if not other_attachment_point_id:
|
||||
error = "Select the other part's attachment point."
|
||||
else:
|
||||
ap_a = await attachment_point_crud.get(db, own_attachment_point_id)
|
||||
ap_b = await attachment_point_crud.get(db, int(other_attachment_point_id))
|
||||
if ap_a is None or ap_b is None:
|
||||
error = "Attachment point not found."
|
||||
elif ap_a.augmented_part_id == ap_b.augmented_part_id:
|
||||
error = "A part cannot fit itself."
|
||||
elif await fit_crud.get_by_attachment_points(db, ap_a, ap_b) is not None:
|
||||
error = "That fit already exists."
|
||||
else:
|
||||
fit = await fit_crud.create(db, ap_a=ap_a, ap_b=ap_b, label=FitLabel.EXACT)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor,
|
||||
action="create",
|
||||
resource_type="Fit",
|
||||
resource_id=fit.id,
|
||||
description=audit_crud.describe_create(
|
||||
"Fit", fit.id, f"attachment points {ap_a.id}↔{ap_b.id}"
|
||||
),
|
||||
)
|
||||
ctx = await _augmented_part_fits_ctx(db, augmented_part_id, error=error)
|
||||
return templates.TemplateResponse(request, "admin/_augmented_part_fits.html", ctx)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/augmented-parts/{augmented_part_id}/fits/{fit_id}/label",
|
||||
response_class=HTMLResponse,
|
||||
)
|
||||
async def update_augmented_part_fit_label(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
fit_id: int,
|
||||
label: FitLabel = Form(...),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("fits:write")),
|
||||
):
|
||||
fit = await fit_crud.get(db, fit_id)
|
||||
if fit is None:
|
||||
raise HTTPException(status_code=404, detail="Fit not found")
|
||||
fit = await fit_crud.set_label(db, fit, label)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="Fit",
|
||||
resource_id=fit.id,
|
||||
description=audit_crud.describe_update("Fit", fit.id, {"label": label.value}),
|
||||
)
|
||||
view = await _fit_view(db, fit, augmented_part_id)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_augmented_part_fit_row.html",
|
||||
{"augmented_part_id": augmented_part_id, **view},
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/augmented-parts/{augmented_part_id}/fits/{fit_id}/notes",
|
||||
response_class=HTMLResponse,
|
||||
)
|
||||
async def create_augmented_part_fit_note(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
fit_id: int,
|
||||
body: str = Form(...),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("fits:write")),
|
||||
):
|
||||
fit = await fit_crud.get(db, fit_id)
|
||||
if fit is None:
|
||||
raise HTTPException(status_code=404, detail="Fit not found")
|
||||
if body.strip():
|
||||
await fit_note_crud.create(db, fit=fit, author=actor, body=body.strip())
|
||||
await db.refresh(fit, attribute_names=["notes"])
|
||||
view = await _fit_view(db, fit, augmented_part_id)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_augmented_part_fit_row.html",
|
||||
{"augmented_part_id": augmented_part_id, **view},
|
||||
)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/augmented-parts/{augmented_part_id}/fits/{fit_id}", response_class=HTMLResponse
|
||||
)
|
||||
async def delete_augmented_part_fit(
|
||||
request: Request,
|
||||
augmented_part_id: int,
|
||||
fit_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("fits:delete")),
|
||||
):
|
||||
fit = await fit_crud.get(db, fit_id)
|
||||
if fit is not None:
|
||||
label = f"attachment points {fit.attachment_point_a_id}↔{fit.attachment_point_b_id}"
|
||||
await fit_crud.delete(db, fit)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor,
|
||||
action="delete",
|
||||
resource_type="Fit",
|
||||
resource_id=fit_id,
|
||||
description=audit_crud.describe_delete("Fit", fit_id, label),
|
||||
)
|
||||
ctx = await _augmented_part_fits_ctx(db, augmented_part_id)
|
||||
return templates.TemplateResponse(request, "admin/_augmented_part_fits.html", ctx)
|
||||
|
||||
|
||||
@router.post("/augmented-parts/fits/recompute", response_class=HTMLResponse)
|
||||
async def recompute_all_fits(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("fits:write")),
|
||||
):
|
||||
result = await fit_matching_service.recompute_all(db)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="Fit",
|
||||
resource_id=None,
|
||||
description=(
|
||||
f"Recomputed fits for all parts: {result.created} created, "
|
||||
f"{result.removed} removed"
|
||||
),
|
||||
)
|
||||
return HTMLResponse(
|
||||
f"Recompute complete: {result.created} created, {result.removed} removed."
|
||||
)
|
||||
|
||||
|
||||
# --- Part Colours ------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user