generated from john/python-template
@@ -22,7 +22,7 @@ Documents manages the archival record for each historical artifact independently
|
|||||||
- The title is **Archival Documents**.
|
- The title is **Archival Documents**.
|
||||||
- **Create new document** opens the create route.
|
- **Create new document** opens the create route.
|
||||||
- The table defaults to Document Title order and supports search and column sorting.
|
- The table defaults to Document Title order and supports search and column sorting.
|
||||||
- Columns are Document Title, Type, Author, Document Date, and # Sources.
|
- Columns are Document Title, Author, Tags, Document Date, Type, and # Sources.
|
||||||
- Document Title is left-aligned; the remaining columns are centered.
|
- Document Title is left-aligned; the remaining columns are centered.
|
||||||
- Author lists all linked people in the `author` role.
|
- Author lists all linked people in the `author` role.
|
||||||
- # Sources reflects the count of linked Source rows for each Document.
|
- # Sources reflects the count of linked Source rows for each Document.
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class DocumentTableRow:
|
|||||||
name: str
|
name: str
|
||||||
document_type: str
|
document_type: str
|
||||||
authors: str
|
authors: str
|
||||||
|
tags: str
|
||||||
document_date: str
|
document_date: str
|
||||||
source_count: int
|
source_count: int
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ def _serialize_rows(rows: Sequence[DocumentTableRow]) -> list[dict[str, Any]]:
|
|||||||
"name": row.name,
|
"name": row.name,
|
||||||
"document_type": row.document_type or "Unspecified",
|
"document_type": row.document_type or "Unspecified",
|
||||||
"authors": row.authors or "Not set",
|
"authors": row.authors or "Not set",
|
||||||
|
"tags": row.tags or "Not tagged",
|
||||||
"document_date": row.document_date,
|
"document_date": row.document_date,
|
||||||
"source_count": row.source_count,
|
"source_count": row.source_count,
|
||||||
}
|
}
|
||||||
@@ -59,15 +61,6 @@ def render_documents_table(rows: Sequence[DocumentTableRow]) -> None:
|
|||||||
"align": "left",
|
"align": "left",
|
||||||
"style": "width: 30%;",
|
"style": "width: 30%;",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "document_type",
|
|
||||||
"label": "Type",
|
|
||||||
"field": "document_type",
|
|
||||||
"sortable": True,
|
|
||||||
"classes": "ui-table-cell-wrap",
|
|
||||||
"align": "center",
|
|
||||||
"style": "width: 14%;",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "authors",
|
"name": "authors",
|
||||||
"label": "Author",
|
"label": "Author",
|
||||||
@@ -75,7 +68,16 @@ def render_documents_table(rows: Sequence[DocumentTableRow]) -> None:
|
|||||||
"sortable": True,
|
"sortable": True,
|
||||||
"classes": "ui-table-cell-wrap",
|
"classes": "ui-table-cell-wrap",
|
||||||
"align": "center",
|
"align": "center",
|
||||||
"style": "width: 22%;",
|
"style": "width: 18%;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tags",
|
||||||
|
"label": "Tags",
|
||||||
|
"field": "tags",
|
||||||
|
"sortable": True,
|
||||||
|
"classes": "ui-table-cell-wrap",
|
||||||
|
"align": "center",
|
||||||
|
"style": "width: 16%;",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "document_date",
|
"name": "document_date",
|
||||||
@@ -86,6 +88,15 @@ def render_documents_table(rows: Sequence[DocumentTableRow]) -> None:
|
|||||||
"align": "center",
|
"align": "center",
|
||||||
"style": "width: 14%;",
|
"style": "width: 14%;",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "document_type",
|
||||||
|
"label": "Type",
|
||||||
|
"field": "document_type",
|
||||||
|
"sortable": True,
|
||||||
|
"classes": "ui-table-cell-wrap",
|
||||||
|
"align": "center",
|
||||||
|
"style": "width: 12%;",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "source_count",
|
"name": "source_count",
|
||||||
"label": "# Sources",
|
"label": "# Sources",
|
||||||
@@ -93,7 +104,7 @@ def render_documents_table(rows: Sequence[DocumentTableRow]) -> None:
|
|||||||
"sortable": True,
|
"sortable": True,
|
||||||
"classes": "font-mono",
|
"classes": "font-mono",
|
||||||
"align": "center",
|
"align": "center",
|
||||||
"style": "width: 20%;",
|
"style": "width: 10%;",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default_sort_by="name",
|
default_sort_by="name",
|
||||||
|
|||||||
@@ -193,9 +193,10 @@ def register_page() -> None: # noqa: PLR0915
|
|||||||
DocumentTableRow(
|
DocumentTableRow(
|
||||||
id=doc.id,
|
id=doc.id,
|
||||||
name=doc.name,
|
name=doc.name,
|
||||||
document_type=(doc.document_type_ref.label if doc.document_type_ref is not None else ""),
|
|
||||||
authors=", ".join(_author_names(doc)),
|
authors=", ".join(_author_names(doc)),
|
||||||
|
tags=", ".join(_tag_labels(doc)),
|
||||||
document_date=compact_date(doc.document_date, doc.document_date_raw),
|
document_date=compact_date(doc.document_date, doc.document_date_raw),
|
||||||
|
document_type=(doc.document_type_ref.label if doc.document_type_ref is not None else ""),
|
||||||
source_count=len(doc.sources),
|
source_count=len(doc.sources),
|
||||||
)
|
)
|
||||||
for doc in documents
|
for doc in documents
|
||||||
@@ -628,6 +629,17 @@ def _author_names(document: Document) -> list[str]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _tag_labels(document: Document) -> list[str]:
|
||||||
|
return sorted(
|
||||||
|
(
|
||||||
|
link.tag_ref.label
|
||||||
|
for link in document.document_tags
|
||||||
|
if link.tag_ref is not None and link.tag_ref.label.strip()
|
||||||
|
),
|
||||||
|
key=str.casefold,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_selected_tag_labels(value: object) -> list[str]:
|
def _resolve_selected_tag_labels(value: object) -> list[str]:
|
||||||
def flatten(item: object) -> list[str]:
|
def flatten(item: object) -> list[str]:
|
||||||
if item is None:
|
if item is None:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Tests for the documents page routes and action handlers."""
|
"""Tests for the documents page routes and action handlers."""
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
@@ -13,6 +14,8 @@ from transcription.db.models import DocumentType
|
|||||||
from transcription.db.models import Job
|
from transcription.db.models import Job
|
||||||
from transcription.db.models import Person
|
from transcription.db.models import Person
|
||||||
from transcription.db.models import PersonRole
|
from transcription.db.models import PersonRole
|
||||||
|
from transcription.db.models import Tag
|
||||||
|
from transcription.db.models import DocumentTag
|
||||||
from transcription.db.models import Source
|
from transcription.db.models import Source
|
||||||
from transcription.ui.pages.documents_page import _resolve_selected_tag_labels
|
from transcription.ui.pages.documents_page import _resolve_selected_tag_labels
|
||||||
|
|
||||||
@@ -69,12 +72,15 @@ class TestDocumentsPageRendering:
|
|||||||
|
|
||||||
async with session_scope() as session:
|
async with session_scope() as session:
|
||||||
postcard_type = (await session.exec(select(DocumentType).where(DocumentType.label == "Postcard"))).one()
|
postcard_type = (await session.exec(select(DocumentType).where(DocumentType.label == "Postcard"))).one()
|
||||||
|
family_tag = Tag(label="Family", normalized_label="family")
|
||||||
doc = Document(
|
doc = Document(
|
||||||
name="1924 Postcard",
|
name="1924 Postcard",
|
||||||
document_type_id=postcard_type.id,
|
document_type_id=postcard_type.id,
|
||||||
archive_identifier="PC-001",
|
archive_identifier="PC-001",
|
||||||
)
|
)
|
||||||
session.add(doc)
|
session.add_all([doc, family_tag])
|
||||||
|
await session.flush()
|
||||||
|
session.add(DocumentTag(document_id=doc.id, tag_id=family_tag.id))
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
response = client.get("/ui/documents")
|
response = client.get("/ui/documents")
|
||||||
@@ -84,8 +90,20 @@ class TestDocumentsPageRendering:
|
|||||||
assert "Postcard" in response.text
|
assert "Postcard" in response.text
|
||||||
assert "Document Date" in response.text
|
assert "Document Date" in response.text
|
||||||
assert "Author" in response.text
|
assert "Author" in response.text
|
||||||
|
assert "Tags" in response.text
|
||||||
|
assert "Family" in response.text
|
||||||
assert "# Sources" in response.text
|
assert "# Sources" in response.text
|
||||||
assert "Archive Ref" not in response.text
|
assert "Archive Ref" not in response.text
|
||||||
|
assert re.search(
|
||||||
|
r'"name":"name","label":"Document Title".*'
|
||||||
|
r'"name":"authors","label":"Author".*'
|
||||||
|
r'"name":"tags","label":"Tags".*'
|
||||||
|
r'"name":"document_date","label":"Document Date".*'
|
||||||
|
r'"name":"document_type","label":"Type".*'
|
||||||
|
r'"name":"source_count","label":"# Sources"',
|
||||||
|
response.text,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
def test_document_create_page_renders_form(self, app_client):
|
def test_document_create_page_renders_form(self, app_client):
|
||||||
_, client = app_client
|
_, client = app_client
|
||||||
|
|||||||
Reference in New Issue
Block a user