Adding on_website and preferred part indicators

This commit is contained in:
Ian Roddis committed 2026-09-21 14:01:23 -03:00
1 parent 13b64a2d17
commit 4fd2bb1b58
13 files changed
+331 -6

No files matched your search

+2
View File
@@ -59,6 +59,8 @@ async def create_augmented_part(
scope_model_id=payload.scope_model_id,
part_type_id=payload.part_type_id,
sub_part_type_id=payload.sub_part_type_id,
on_website=payload.on_website,
preferred_part=payload.preferred_part,
)
await audit_crud.log(
db,
+18 -1
View File
@@ -10,7 +10,14 @@ from app.models.part_type import PartType
from app.models.scope_model import ScopeModel
from app.models.sub_part_type import SubPartType
_RELATIONSHIPS = ["colour", "manufacturer", "scope_model", "part_type", "sub_part_type"]
_RELATIONSHIPS = [
"colour",
"manufacturer",
"scope_model",
"part_type",
"sub_part_type",
"notes",
]
# Sortable columns for the admin list, keyed by the query-string "sort" value.
# Columns backed by a related table's name require an outer join (the FK may be
@@ -87,6 +94,7 @@ async def list_filtered(
part_type_id: int | None = None,
sub_part_type_id: int | None = None,
include_archived: bool = False,
on_website: bool = False,
sort: str = "description",
direction: str = "asc",
skip: int = 0,
@@ -110,6 +118,8 @@ async def list_filtered(
q = q.where(AugmentedPart.sub_part_type_id == sub_part_type_id)
if not include_archived:
q = q.where(AugmentedPart.archived.is_(False))
if on_website:
q = q.where(AugmentedPart.on_website.is_(True))
q = _apply_sort(q, sort, direction)
result = await db.execute(q.offset(skip).limit(limit))
return list(result.scalars().all())
@@ -124,6 +134,7 @@ async def count_filtered(
part_type_id: int | None = None,
sub_part_type_id: int | None = None,
include_archived: bool = False,
on_website: bool = False,
) -> int:
from sqlalchemy import func
@@ -145,6 +156,8 @@ async def count_filtered(
q = q.where(AugmentedPart.sub_part_type_id == sub_part_type_id)
if not include_archived:
q = q.where(AugmentedPart.archived.is_(False))
if on_website:
q = q.where(AugmentedPart.on_website.is_(True))
result = await db.execute(q)
return result.scalar_one()
@@ -220,6 +233,8 @@ async def create(
scope_model_id: int | None,
part_type_id: int,
sub_part_type_id: int | None,
on_website: bool = False,
preferred_part: bool = False,
) -> AugmentedPart:
ap = AugmentedPart(
sku=sku,
@@ -229,6 +244,8 @@ async def create(
scope_model_id=scope_model_id,
part_type_id=part_type_id,
sub_part_type_id=sub_part_type_id,
on_website=on_website,
preferred_part=preferred_part,
)
db.add(ap)
await db.commit()
+2
View File
@@ -47,6 +47,8 @@ class AugmentedPart(Base):
# removed, so fit/attachment-point history is preserved. Hidden from list
# views by default.
archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
on_website: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
preferred_part: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
# linked imported parts — not eagerly loaded; used by sync service only
imported_parts: Mapped[list[ImportedPart]] = relationship(
+4
View File
@@ -39,6 +39,8 @@ class AugmentedPartBase(BaseModel):
scope_model_id: int | None = None
part_type_id: int
sub_part_type_id: int | None = None
on_website: bool = False
preferred_part: bool = False
class AugmentedPartCreate(AugmentedPartBase):
@@ -54,6 +56,8 @@ class AugmentedPartUpdate(BaseModel):
part_type_id: int | None = None
sub_part_type_id: int | None = None
archived: bool | None = None
on_website: bool | None = None
preferred_part: bool | None = None
class AugmentedPartSyncResult(BaseModel):
@@ -8,6 +8,8 @@
<td>{{ ap.part_type.name }}</td>
<td>{{ ap.sub_part_type.name if ap.sub_part_type else '—' }}</td>
<td>{% if ap.archived %}<span class="tag">Archived</span>{% else %}—{% endif %}</td>
<td>{% if ap.on_website %}<span class="tag">Yes</span>{% else %}—{% endif %}</td>
<td>{% if ap.preferred_part %}<span class="tag">Yes</span>{% else %}—{% endif %}</td>
<td class="row-actions">
<button class="btn btn-ghost"
hx-get="/admin/augmented-parts/{{ ap.id }}/edit"
@@ -1,5 +1,5 @@
<tr id="augmented-part-{{ ap.id }}">
<td colspan="10">
<td colspan="12">
<form id="augmented-part-edit-form-{{ ap.id }}"
hx-post="/admin/augmented-parts/{{ ap.id }}"
hx-target="#augmented-part-{{ ap.id }}"
@@ -45,6 +45,8 @@
{% endfor %}
</select>
</label>
<label class="check"><input type="checkbox" name="on_website" value="true" {{ "checked" if ap.on_website }}> On website</label>
<label class="check"><input type="checkbox" name="preferred_part" value="true" {{ "checked" if ap.preferred_part }}> Preferred part</label>
<div class="row-actions">
<button type="submit" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-ghost"
@@ -44,6 +44,7 @@
{% endfor %}
</select>
<label><input type="checkbox" name="include_archived" {{ "checked" if include_archived }}> Show archived</label>
<label><input type="checkbox" name="on_website" {{ "checked" if on_website }}> On website</label>
</form>
</div>
@@ -13,6 +13,8 @@
{{ sort_th("part_type", "Type", sort_headers) }}
{{ sort_th("sub_part_type", "Sub Type", sort_headers) }}
<th>Archived</th>
<th>On Website</th>
<th>Preferred</th>
<th></th>
</tr>
</thead>
@@ -20,7 +22,7 @@
{% for ap in augmented_parts %}
{% include "admin/_augmented_part_row.html" %}
{% else %}
<tr><td colspan="10" class="muted">No augmented parts recorded.</td></tr>
<tr><td colspan="12" class="muted">No augmented parts recorded.</td></tr>
{% endfor %}
</tbody>
</table>
@@ -11,6 +11,8 @@
<span class="tag">{{ ap.part_type.name }}</span>
{% if ap.sub_part_type %}<span class="tag">{{ ap.sub_part_type.name }}</span>{% endif %}
{% if ap.archived %}<span class="tag">Archived</span>{% endif %}
{% if ap.on_website %}<span class="tag">On website</span>{% endif %}
{% if ap.preferred_part %}<span class="tag">Preferred</span>{% endif %}
{% if ap.description %}{{ ap.description }}{% endif %}
</p>
+2
View File
@@ -50,6 +50,8 @@
{% endfor %}
</select>
</label>
<label class="check"><input type="checkbox" name="on_website" value="true"> On website</label>
<label class="check"><input type="checkbox" name="preferred_part" value="true"> Preferred part</label>
<button type="submit" class="btn btn-primary">Add</button>
</form>
</div>
+25 -3
View File
@@ -1674,6 +1674,7 @@ async def _default_augmented_parts_table_ctx(db: AsyncSession) -> dict:
"sort": "description",
"dir": "asc",
"include_archived": False,
"on_website": False,
}
@@ -1683,23 +1684,30 @@ async def augmented_parts_page(
sort: str = "description",
dir: str = "asc",
include_archived: bool = False,
on_website: bool = False,
db: AsyncSession = Depends(get_db),
user: User = Depends(web_require("augmented_parts:read")),
):
ctx = await _augmented_part_form_ctx(db)
total = await augmented_part_crud.count_filtered(
db, include_archived=include_archived
db, include_archived=include_archived, on_website=on_website
)
ctx["augmented_parts"] = await augmented_part_crud.list_filtered(
db,
sort=sort,
direction=dir,
include_archived=include_archived,
on_website=on_website,
limit=_PAGE_SIZE,
)
ctx["pagination"] = _paginate(
"/admin/augmented-parts/filter",
{"sort": sort, "dir": dir, "include_archived": include_archived},
{
"sort": sort,
"dir": dir,
"include_archived": include_archived,
"on_website": on_website,
},
1,
_PAGE_SIZE,
total,
@@ -1707,7 +1715,7 @@ async def augmented_parts_page(
)
ctx["sort_headers"] = _sort_headers(
"/admin/augmented-parts/filter",
{"include_archived": include_archived},
{"include_archived": include_archived, "on_website": on_website},
_AUGMENTED_PARTS_SORT_COLUMNS,
sort,
dir,
@@ -1716,6 +1724,7 @@ async def augmented_parts_page(
ctx["sort"] = sort
ctx["dir"] = dir
ctx["include_archived"] = include_archived
ctx["on_website"] = on_website
ctx["user"] = user
return templates.TemplateResponse(request, "admin/augmented_parts.html", ctx)
@@ -1730,6 +1739,8 @@ async def create_augmented_part(
scope_model_id: str = Form(""),
part_type_id: int = Form(...),
sub_part_type_id: str = Form(""),
on_website: bool = Form(False),
preferred_part: bool = Form(False),
db: AsyncSession = Depends(get_db),
actor: User = Depends(web_require("augmented_parts:write")),
):
@@ -1745,6 +1756,8 @@ async def create_augmented_part(
scope_model_id=int(scope_model_id) if scope_model_id else None,
part_type_id=part_type_id,
sub_part_type_id=int(sub_part_type_id) if sub_part_type_id else None,
on_website=on_website,
preferred_part=preferred_part,
)
await audit_crud.log(
db,
@@ -1874,6 +1887,7 @@ async def filter_augmented_parts(
part_type_id: str = "",
sub_part_type_id: str = "",
include_archived: bool = False,
on_website: bool = False,
sort: str = "description",
dir: str = "asc",
page: int = 1,
@@ -1895,6 +1909,7 @@ async def filter_augmented_parts(
part_type_id=part_type_id_int,
sub_part_type_id=sub_part_type_id_int,
include_archived=include_archived,
on_website=on_website,
)
filter_params = {
"description": description,
@@ -1904,6 +1919,7 @@ async def filter_augmented_parts(
"part_type_id": part_type_id,
"sub_part_type_id": sub_part_type_id,
"include_archived": include_archived,
"on_website": on_website,
}
pagination = _paginate(
"/admin/augmented-parts/filter",
@@ -1922,6 +1938,7 @@ async def filter_augmented_parts(
part_type_id=part_type_id_int,
sub_part_type_id=sub_part_type_id_int,
include_archived=include_archived,
on_website=on_website,
sort=sort,
direction=dir,
skip=pagination["skip"],
@@ -1945,6 +1962,7 @@ async def filter_augmented_parts(
"sort": sort,
"dir": dir,
"include_archived": include_archived,
"on_website": on_website,
},
)
@@ -2188,6 +2206,8 @@ async def update_augmented_part(
scope_model_id: str = Form(""),
part_type_id: int = Form(...),
sub_part_type_id: str = Form(""),
on_website: bool = Form(False),
preferred_part: bool = Form(False),
db: AsyncSession = Depends(get_db),
actor: User = Depends(web_require("augmented_parts:write")),
):
@@ -2207,6 +2227,8 @@ async def update_augmented_part(
"scope_model_id": int(scope_model_id) if scope_model_id else None,
"part_type_id": part_type_id,
"sub_part_type_id": int(sub_part_type_id) if sub_part_type_id else None,
"on_website": on_website,
"preferred_part": preferred_part,
}
await augmented_part_crud.update(db, ap, changes)
await audit_crud.log(