From 4f10978989619b8b2b87f61490e09ce4e2659e49 Mon Sep 17 00:00:00 2001 From: McElwain Date: Mon, 6 Apr 2026 16:31:40 -0500 Subject: [PATCH] refactor: use shared sidebar partial across templates --- app/routes/documents.py | 3 +- app/routes/ingest.py | 1 + app/routes/line_items.py | 298 ++++++++++++++ app/routes/queue.py | 1 + app/routes/trash.py | 2 +- app/templates/documents/detail.html | 17 +- .../documents/detail.html.bak_sidebar | 362 ++++++++++++++++++ app/templates/documents/list.html | 17 +- app/templates/documents/list.html.bak_sidebar | 95 +++++ app/templates/ingest/index.html | 17 +- app/templates/ingest/index.html.bak_sidebar | 105 +++++ app/templates/line_items/list.html | 144 +++++++ .../line_items/list.html.bak_sidebar | 161 ++++++++ app/templates/line_items/summary.html | 93 +++++ .../line_items/summary.html.bak_sidebar | 110 ++++++ app/templates/partials/sidebar.html | 18 + app/templates/queue/index.html | 17 +- app/templates/queue/index.html.bak_sidebar | 134 +++++++ app/templates/trash/index.html | 17 +- app/templates/trash/index.html.bak_sidebar | 97 +++++ 20 files changed, 1627 insertions(+), 82 deletions(-) create mode 100644 app/routes/line_items.py create mode 100644 app/templates/documents/detail.html.bak_sidebar create mode 100644 app/templates/documents/list.html.bak_sidebar create mode 100644 app/templates/ingest/index.html.bak_sidebar create mode 100644 app/templates/line_items/list.html create mode 100644 app/templates/line_items/list.html.bak_sidebar create mode 100644 app/templates/line_items/summary.html create mode 100644 app/templates/line_items/summary.html.bak_sidebar create mode 100644 app/templates/partials/sidebar.html create mode 100644 app/templates/queue/index.html.bak_sidebar create mode 100644 app/templates/trash/index.html.bak_sidebar diff --git a/app/routes/documents.py b/app/routes/documents.py index 3a18a6c..1960312 100644 --- a/app/routes/documents.py +++ b/app/routes/documents.py @@ -233,7 +233,7 @@ def list_documents(request: Request, db: Session = Depends(get_db)): return templates.TemplateResponse( request=request, name="documents/list.html", - context={"request": request, "documents": documents}, + context={"request": request, "documents": documents, "active_page": "documents"}, ) @@ -507,5 +507,6 @@ def document_detail(document_id: str, request: Request, queue: str | None = None "error_actual": error_actual, "extracted_form": extracted_form, "current_extracted": current_extracted, + "active_page": "documents", }, ) diff --git a/app/routes/ingest.py b/app/routes/ingest.py index bce03fd..98cdb7a 100644 --- a/app/routes/ingest.py +++ b/app/routes/ingest.py @@ -23,6 +23,7 @@ def ingest_home(request: Request): context={ "request": request, "inbox_root": INBOX_ROOT, + "active_page": "ingest", }, ) diff --git a/app/routes/line_items.py b/app/routes/line_items.py new file mode 100644 index 0000000..3626dea --- /dev/null +++ b/app/routes/line_items.py @@ -0,0 +1,298 @@ +from pathlib import Path +from decimal import Decimal, InvalidOperation + +from fastapi import APIRouter, Depends, Form, Query, Request +from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.templating import Jinja2Templates +from sqlalchemy import func +from sqlalchemy.orm import Session, selectinload + +from app.db.deps import get_db +from app.logic.extraction import get_current_extracted_fields +from app.models.document import Document +from app.models.receipt_line_item import ReceiptLineItem + +router = APIRouter(prefix="/line-items", tags=["line-items"]) + +BASE_DIR = Path(__file__).resolve().parent.parent +templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) + + +def _decimal_to_str(value: Decimal | None) -> str: + if value is None: + return "" + return str(value) + + +def _to_decimal(value: str | None) -> Decimal | None: + if value is None: + return None + cleaned = str(value).strip() + if not cleaned: + return None + try: + return Decimal(cleaned) + except (InvalidOperation, TypeError): + return None + + +def _line_item_quality_rating(item: ReceiptLineItem) -> str: + extra = item.extra_json or {} + value = extra.get("quality_rating") + return "" if value is None else str(value) + + +def _line_item_quality_note(item: ReceiptLineItem) -> str: + extra = item.extra_json or {} + value = extra.get("quality_note") + return "" if value is None else str(value) + + +@router.post("/{line_item_id}/review", response_class=RedirectResponse) +def save_line_item_review( + line_item_id: int, + q: str = Form(""), + merchant: str = Form(""), + category: str = Form(""), + date_from: str = Form(""), + date_to: str = Form(""), + rating_min: str = Form(""), + rating_max: str = Form(""), + quality_rating: str = Form(""), + quality_note: str = Form(""), + db: Session = Depends(get_db), +): + item = db.query(ReceiptLineItem).filter(ReceiptLineItem.id == line_item_id).first() + if item is None: + return RedirectResponse(url="/line-items/", status_code=303) + + extra = dict(item.extra_json or {}) + + rating_clean = quality_rating.strip() + note_clean = quality_note.strip() + + if rating_clean: + extra["quality_rating"] = rating_clean + else: + extra.pop("quality_rating", None) + + if note_clean: + extra["quality_note"] = note_clean + else: + extra.pop("quality_note", None) + + item.extra_json = extra + db.commit() + + redirect_url = ( + f"/line-items/?q={q}&merchant={merchant}&category={category}" + f"&date_from={date_from}&date_to={date_to}" + f"&rating_min={rating_min}&rating_max={rating_max}" + ) + return RedirectResponse(url=redirect_url, status_code=303) + + +@router.get("/", response_class=HTMLResponse) +def list_line_items( + request: Request, + q: str = Query("", description="Item description contains"), + merchant: str = Query("", description="Merchant contains"), + category: str = Query("", description="Category equals"), + date_from: str = Query("", description="YYYY-MM-DD"), + date_to: str = Query("", description="YYYY-MM-DD"), + rating_min: str = Query("", description="Minimum rating"), + rating_max: str = Query("", description="Maximum rating"), + db: Session = Depends(get_db), +): + items = ( + db.query(ReceiptLineItem) + .options( + selectinload(ReceiptLineItem.document).selectinload(Document.extracted_fields) + ) + .order_by(ReceiptLineItem.id.desc()) + .all() + ) + + q_norm = q.strip().lower() + merchant_norm = merchant.strip().lower() + category_norm = category.strip().lower() + rating_min_dec = _to_decimal(rating_min) + rating_max_dec = _to_decimal(rating_max) + + rows: list[dict] = [] + + for item in items: + document = item.document + if document is None: + continue + + extracted = get_current_extracted_fields(document) + merchant_value = "" + transaction_date = "" + + if extracted is not None: + merchant_value = ( + extracted.merchant_normalized + or extracted.merchant_raw + or "" + ) + if extracted.transaction_date: + transaction_date = extracted.transaction_date.isoformat() + + if not transaction_date and document.created_at: + transaction_date = document.created_at.date().isoformat() + + description_value = ( + item.normalized_description + or item.raw_description + or "" + ) + category_value = item.item_category or "" + quality_rating_value = _line_item_quality_rating(item) + quality_note_value = _line_item_quality_note(item) + quality_rating_dec = _to_decimal(quality_rating_value) + + if q_norm and q_norm not in description_value.lower(): + continue + if merchant_norm and merchant_norm not in merchant_value.lower(): + continue + if category_norm and category_norm not in category_value.lower(): + continue + if date_from and (not transaction_date or transaction_date < date_from): + continue + if date_to and (not transaction_date or transaction_date > date_to): + continue + if rating_min_dec is not None: + if quality_rating_dec is None or quality_rating_dec < rating_min_dec: + continue + if rating_max_dec is not None: + if quality_rating_dec is None or quality_rating_dec > rating_max_dec: + continue + + rows.append( + { + "line_item_id": item.id, + "document_id": document.document_id, + "transaction_date": transaction_date, + "merchant": merchant_value, + "description": description_value, + "raw_description": item.raw_description or "", + "quantity": _decimal_to_str(item.quantity), + "line_total": _decimal_to_str(item.line_total), + "category": category_value, + "confidence": _decimal_to_str(item.confidence), + "quality_rating": quality_rating_value, + "quality_note": quality_note_value, + } + ) + + rows.sort( + key=lambda row: ( + row["transaction_date"] or "", + row["merchant"] or "", + row["description"] or "", + ), + reverse=True, + ) + + return templates.TemplateResponse( + request=request, + name="line_items/list.html", + context={ + "request": request, + "rows": rows, + "q": q, + "merchant": merchant, + "category": category, + "date_from": date_from, + "date_to": date_to, + "rating_min": rating_min, + "rating_max": rating_max, + "active_page": "line_items", + }, + ) + + +@router.get("/summary", response_class=HTMLResponse) +def summarize_line_items( + request: Request, + q: str = Query("", description="Item contains"), + db: Session = Depends(get_db), +): + query = ( + db.query( + ReceiptLineItem.normalized_description.label("item"), + func.count().label("count"), + func.avg(ReceiptLineItem.line_total).label("avg_price"), + func.min(ReceiptLineItem.line_total).label("min_price"), + func.max(ReceiptLineItem.line_total).label("max_price"), + ) + ) + + if q: + query = query.filter( + ReceiptLineItem.normalized_description.ilike(f"%{q}%") + ) + + query = query.group_by(ReceiptLineItem.normalized_description) + results = query.all() + + rating_query = db.query( + ReceiptLineItem.normalized_description, + ReceiptLineItem.extra_json, + ) + if q: + rating_query = rating_query.filter( + ReceiptLineItem.normalized_description.ilike(f"%{q}%") + ) + rating_rows = rating_query.all() + + rating_map: dict[str, dict[str, Decimal | int]] = {} + + for item_name, extra_json in rating_rows: + key = item_name or "" + rating_info = rating_map.setdefault( + key, + {"rated_count": 0, "rating_sum": Decimal("0")} + ) + extra = extra_json or {} + rating_dec = _to_decimal(extra.get("quality_rating")) + if rating_dec is not None: + rating_info["rated_count"] += 1 + rating_info["rating_sum"] += rating_dec + + rows = [] + for r in results: + item_name = r.item or "" + rating_info = rating_map.get(item_name, {"rated_count": 0, "rating_sum": Decimal("0")}) + rated_count = int(rating_info["rated_count"]) + rating_sum = rating_info["rating_sum"] + + avg_rating = "" + if rated_count > 0: + avg_rating = str((rating_sum / rated_count).quantize(Decimal("0.01"))) + + rows.append( + { + "item": item_name, + "count": r.count, + "avg_price": str(round(r.avg_price, 2)) if r.avg_price is not None else "", + "min_price": str(r.min_price) if r.min_price is not None else "", + "max_price": str(r.max_price) if r.max_price is not None else "", + "rated_count": rated_count, + "avg_rating": avg_rating, + } + ) + + rows.sort(key=lambda x: (x["count"], x["item"]), reverse=True) + + return templates.TemplateResponse( + request=request, + name="line_items/summary.html", + context={ + "request": request, + "rows": rows, + "q": q, + "active_page": "line_item_summary", + }, + ) diff --git a/app/routes/queue.py b/app/routes/queue.py index 87855f2..f616f9e 100644 --- a/app/routes/queue.py +++ b/app/routes/queue.py @@ -54,5 +54,6 @@ def review_queue(request: Request, db: Session = Depends(get_db)): "recently_updated": recently_updated, "next_ocr": next_ocr, "next_fields": next_fields, + "active_page": "queue", }, ) diff --git a/app/routes/trash.py b/app/routes/trash.py index 1457476..be1a342 100644 --- a/app/routes/trash.py +++ b/app/routes/trash.py @@ -30,7 +30,7 @@ def trash_index(request: Request, db: Session = Depends(get_db)): return templates.TemplateResponse( request=request, name="trash/index.html", - context={"request": request, "documents": documents}, + context={"request": request, "documents": documents, "active_page": "trash"}, ) diff --git a/app/templates/documents/detail.html b/app/templates/documents/detail.html index 7daea7b..df1018c 100644 --- a/app/templates/documents/detail.html +++ b/app/templates/documents/detail.html @@ -7,22 +7,7 @@
- + {% include "partials/sidebar.html" %}
{% if error == "line_count_mismatch" %} diff --git a/app/templates/documents/detail.html.bak_sidebar b/app/templates/documents/detail.html.bak_sidebar new file mode 100644 index 0000000..a3293f6 --- /dev/null +++ b/app/templates/documents/detail.html.bak_sidebar @@ -0,0 +1,362 @@ + + + + + {{ document.document_id }} + + + +
+ + +
+ {% if error == "line_count_mismatch" %} +
+ Could not save reviewed OCR because line count did not match OCR layout. + Expected {{ error_expected }}, got {{ error_actual }}. +
+ {% elif error == "save_ocr_corrected_failed" %} +
+ Could not save OCR-corrected PDF. Check that reviewed OCR line count matches raw OCR line count. +
+ {% elif error == "rerun_ocr_failed" %} +
OCR rerun failed.
+ {% elif error == "save_field_enriched_failed" %} +
Could not save field-enriched PDF.
+ {% endif %} + +
+
+
+

{{ document.document_id }}

+

{{ document.original_filename or document.canonical_filename or document.document_type }}

+
+
+ {{ document.review_status }} + {{ document.document_type }} + {{ document.mime_type }} +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ {% if prev_doc %} + ← Previous + {% endif %} + {% if next_doc %} + Next → + {% endif %} + {% if next_ocr_doc %} + Next OCR review + {% endif %} + {% if next_fields_doc %} + Next field extraction + {% endif %} +
+
+
+ +
+
+
+

Document preview

+ {% if file_url %} + {% if document.mime_type == "application/pdf" %} + + {% elif document.mime_type in ["image/jpeg", "image/png"] %} + Document image + {% else %} +

Open file

+ {% endif %} + {% else %} +

No preview available.

+ {% endif %} +
+
+ +
+
+
+ + + + +
+ +
+

Reviewed OCR

+ {% if reviewed_ocr %} +

Current reviewed version saved at {{ reviewed_ocr.created_at }} — v{{ reviewed_ocr.version_number }}

+ {% else %} +

No reviewed OCR saved yet.

+ {% endif %} + +

+ Expected OCR lines: {{ expected_line_count }}
+ Current editor lines: {{ actual_line_count }}
+ + Line count mismatch may affect corrected PDF layout. + +

+ +
+
+ +
+
{% for n in line_numbers %}{{ n }}
+{% endfor %}
+ +
+
+ +
+ +
+ {% for flag in quality_flag_options %} + + {% endfor %} +
+
+ +
+ + +
+ +
+ +
+
+
+ +
+

Extracted fields

+ + {% if current_extracted %} +

Current extracted fields last updated at {{ current_extracted.updated_at }}

+ {% else %} +

No extracted fields saved yet.

+ {% endif %} + +
+ + +
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+ +
+

Document versions

+ {% if document.versions %} +
+ + + + + + + + + + + + {% for version in document.versions %} + + + + + + + + {% endfor %} + +
VersionTypePathCreatedNotes
v{{ version.version_number }}{{ version.version_type }}{{ version.file_path }}{{ version.created_at }}{{ version.notes or "" }}
+
+ {% else %} +

No versions found.

+ {% endif %} +
+ +
+

Raw OCR

+ {% if raw_ocr %} +
+
Text versionv{{ raw_ocr.version_number }}
+
OCR engine{{ raw_ocr.ocr_engine or "unknown" }}
+
Engine version{{ raw_ocr.ocr_engine_version or "unknown" }}
+
Rerun source{{ raw_ocr.rerun_source or "unknown" }}
+
Quality score{{ raw_ocr.quality_score if raw_ocr.quality_score is not none else "not scored yet" }}
+
Quality note{{ raw_ocr.quality_note or "" }}
+
+

Quality flags: {{ raw_ocr.quality_flags if raw_ocr and raw_ocr.quality_flags else [] }}

+
{{ raw_ocr.text_content }}
+ {% else %} +

No raw OCR text found.

+ {% endif %} +
+
+
+
+ +
+

Metadata

+
+
Type{{ document.document_type }}
+
Review status{{ document.review_status }}
+
Source path{{ document.source_path }}
+
Current path{{ document.current_path }}
+
Original filename{{ document.original_filename }}
+
Canonical filename{{ document.canonical_filename }}
+
MIME type{{ document.mime_type }}
+
File size{{ document.file_size }}
+
Page count{{ document.page_count }}
+
Share path{{ document.share_path or "" }}
+
Created at{{ document.created_at }}
+
Updated at{{ document.updated_at }}
+
+
+
+
+ + + + diff --git a/app/templates/documents/list.html b/app/templates/documents/list.html index 53f13bd..4ea6e72 100644 --- a/app/templates/documents/list.html +++ b/app/templates/documents/list.html @@ -7,22 +7,7 @@
- + {% include "partials/sidebar.html" %}
diff --git a/app/templates/documents/list.html.bak_sidebar b/app/templates/documents/list.html.bak_sidebar new file mode 100644 index 0000000..f0de017 --- /dev/null +++ b/app/templates/documents/list.html.bak_sidebar @@ -0,0 +1,95 @@ + + + + + Documents + + + +
+ + +
+
+
+

Documents

+

Active documents available for review and processing.

+
+
+ + + +
+

All documents

+ {% if documents %} +
+ + + + + + + + + + + + {% for doc in documents %} + + + + + + + + {% endfor %} + +
DocumentTypeReview statusCurrent pathUpdated
{{ doc.document_id }}{{ doc.document_type }}{{ doc.review_status }}{{ doc.current_path }}{{ doc.updated_at }}
+
+ {% else %} +

No documents found.

+ {% endif %} +
+
+
+ + + + diff --git a/app/templates/ingest/index.html b/app/templates/ingest/index.html index 5df07bd..7ef0510 100644 --- a/app/templates/ingest/index.html +++ b/app/templates/ingest/index.html @@ -7,22 +7,7 @@
- + {% include "partials/sidebar.html" %}
diff --git a/app/templates/ingest/index.html.bak_sidebar b/app/templates/ingest/index.html.bak_sidebar new file mode 100644 index 0000000..83b4e29 --- /dev/null +++ b/app/templates/ingest/index.html.bak_sidebar @@ -0,0 +1,105 @@ + + + + + Ingest + + + +
+ + +
+
+
+

Ingest

+

Upload files or ingest from server-side paths.

+
+
+ +
+

Upload files

+
+
+ + +
+
+ +
+
+
+ +
+

Server-side ingest

+ +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+ +
+
+
+
+
+ + + + diff --git a/app/templates/line_items/list.html b/app/templates/line_items/list.html new file mode 100644 index 0000000..1c0c761 --- /dev/null +++ b/app/templates/line_items/list.html @@ -0,0 +1,144 @@ + + + + + Line Items + + + +
+ {% include "partials/sidebar.html" %} + +
+
+
+

Line Items

+

Search extracted purchase lines across documents

+
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + Clear + Summary view +
+
+
+ +
+

Results

+ + {% if rows %} + {% for row in rows %} +
+
+
+
{{ row.transaction_date }} · {{ row.merchant }}
+

{{ row.description }}

+
{{ row.raw_description }}
+
+
+ {% if row.category %} + {{ row.category }} + {% endif %} + {% if row.quantity %} + Qty {{ row.quantity }} + {% endif %} + {% if row.line_total %} + ${{ row.line_total }} + {% endif %} + {% if row.confidence %} + Conf {{ row.confidence }} + {% endif %} + {% if row.quality_rating %} + Rating {{ row.quality_rating }} + {% endif %} +
+
+ +
+ + + + + + + + +
+
+ + +
+
+ + +
+
+ +
+ + Open document +
+
+
+ {% endfor %} + {% else %} +

No line items found for the current filters.

+ {% endif %} +
+
+
+ + + + diff --git a/app/templates/line_items/list.html.bak_sidebar b/app/templates/line_items/list.html.bak_sidebar new file mode 100644 index 0000000..b1f9f4e --- /dev/null +++ b/app/templates/line_items/list.html.bak_sidebar @@ -0,0 +1,161 @@ + + + + + Line Items + + + +
+ + +
+
+
+

Line Items

+

Search extracted purchase lines across documents

+
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + Clear + Summary view +
+
+
+ +
+

Results

+ + {% if rows %} + {% for row in rows %} +
+
+
+
{{ row.transaction_date }} · {{ row.merchant }}
+

{{ row.description }}

+
{{ row.raw_description }}
+
+
+ {% if row.category %} + {{ row.category }} + {% endif %} + {% if row.quantity %} + Qty {{ row.quantity }} + {% endif %} + {% if row.line_total %} + ${{ row.line_total }} + {% endif %} + {% if row.confidence %} + Conf {{ row.confidence }} + {% endif %} + {% if row.quality_rating %} + Rating {{ row.quality_rating }} + {% endif %} +
+
+ +
+ + + + + + + + +
+
+ + +
+
+ + +
+
+ +
+ + Open document +
+
+
+ {% endfor %} + {% else %} +

No line items found for the current filters.

+ {% endif %} +
+
+
+ + + + diff --git a/app/templates/line_items/summary.html b/app/templates/line_items/summary.html new file mode 100644 index 0000000..8a6f855 --- /dev/null +++ b/app/templates/line_items/summary.html @@ -0,0 +1,93 @@ + + + + + Line Item Summary + + + +
+ {% include "partials/sidebar.html" %} + +
+
+
+

Line Item Summary

+

Aggregate prices and ratings across extracted line items

+
+
+ +
+
+
+
+ + +
+
+
+ + Clear + Detailed view +
+
+
+ +
+

Summary Results

+ + {% if rows %} +
+ + + + + + + + + + + + + + {% for row in rows %} + + + + + + + + + + {% endfor %} + +
ItemCountAvg PriceMin PriceMax PriceRated CountAvg Rating
{{ row.item }}{{ row.count }}{{ row.avg_price }}{{ row.min_price }}{{ row.max_price }}{{ row.rated_count }}{{ row.avg_rating }}
+
+ {% else %} +

No summary rows found for the current search.

+ {% endif %} +
+
+
+ + + + diff --git a/app/templates/line_items/summary.html.bak_sidebar b/app/templates/line_items/summary.html.bak_sidebar new file mode 100644 index 0000000..79d358f --- /dev/null +++ b/app/templates/line_items/summary.html.bak_sidebar @@ -0,0 +1,110 @@ + + + + + Line Item Summary + + + +
+ + +
+
+
+

Line Item Summary

+

Aggregate prices and ratings across extracted line items

+
+
+ +
+
+
+
+ + +
+
+
+ + Clear + Detailed view +
+
+
+ +
+

Summary Results

+ + {% if rows %} +
+ + + + + + + + + + + + + + {% for row in rows %} + + + + + + + + + + {% endfor %} + +
ItemCountAvg PriceMin PriceMax PriceRated CountAvg Rating
{{ row.item }}{{ row.count }}{{ row.avg_price }}{{ row.min_price }}{{ row.max_price }}{{ row.rated_count }}{{ row.avg_rating }}
+
+ {% else %} +

No summary rows found for the current search.

+ {% endif %} +
+
+
+ + + + diff --git a/app/templates/partials/sidebar.html b/app/templates/partials/sidebar.html new file mode 100644 index 0000000..4552ffb --- /dev/null +++ b/app/templates/partials/sidebar.html @@ -0,0 +1,18 @@ + diff --git a/app/templates/queue/index.html b/app/templates/queue/index.html index 0b17337..26806ce 100644 --- a/app/templates/queue/index.html +++ b/app/templates/queue/index.html @@ -7,22 +7,7 @@
- + {% include "partials/sidebar.html" %}
diff --git a/app/templates/queue/index.html.bak_sidebar b/app/templates/queue/index.html.bak_sidebar new file mode 100644 index 0000000..3b97f40 --- /dev/null +++ b/app/templates/queue/index.html.bak_sidebar @@ -0,0 +1,134 @@ + + + + + Review Queue + + + +
+ + +
+
+
+

Review Queue

+

Work through OCR review and field extraction in order.

+
+
+ +
+
+ {% if next_ocr %} + Next needing OCR review + {% endif %} + {% if next_fields %} + Next needing field extraction + {% endif %} +
+
+ +
+

Needs OCR review ({{ needs_ocr_review|length }})

+ {% if needs_ocr_review %} +
+ + + + {% for doc in needs_ocr_review %} + + + + + + + {% endfor %} + +
DocumentTypeReview statusUpdated
{{ doc.document_id }}{{ doc.document_type }}{{ doc.review_status }}{{ doc.updated_at }}
+
+ {% else %} +

No documents currently need OCR review.

+ {% endif %} +
+ +
+

Needs field extraction ({{ needs_field_extraction|length }})

+ {% if needs_field_extraction %} +
+ + + + {% for doc in needs_field_extraction %} + + + + + + + {% endfor %} + +
DocumentTypeReview statusUpdated
{{ doc.document_id }}{{ doc.document_type }}{{ doc.review_status }}{{ doc.updated_at }}
+
+ {% else %} +

No reviewed documents are waiting on field extraction.

+ {% endif %} +
+ +
+

Recently updated

+ {% if recently_updated %} +
+ + + + {% for doc in recently_updated %} + + + + + + + + {% endfor %} + +
DocumentTypeReview statusCurrent pathUpdated
{{ doc.document_id }}{{ doc.document_type }}{{ doc.review_status }}{{ doc.current_path }}{{ doc.updated_at }}
+
+ {% endif %} +
+
+
+ + + + diff --git a/app/templates/trash/index.html b/app/templates/trash/index.html index 9bae0c2..57652d9 100644 --- a/app/templates/trash/index.html +++ b/app/templates/trash/index.html @@ -7,22 +7,7 @@
- + {% include "partials/sidebar.html" %}
diff --git a/app/templates/trash/index.html.bak_sidebar b/app/templates/trash/index.html.bak_sidebar new file mode 100644 index 0000000..ae68aca --- /dev/null +++ b/app/templates/trash/index.html.bak_sidebar @@ -0,0 +1,97 @@ + + + + + Trash + + + +
+ + +
+
+
+

Trash

+

Soft-deleted documents can be restored or removed permanently.

+
+
+ +
+ {% if documents %} +
+ + + + + + + + + + + + + {% for doc in documents %} + + + + + + + + + {% endfor %} + +
DocumentTypeReview statusTrashed atCurrent pathActions
{{ doc.document_id }}{{ doc.document_type }}{{ doc.review_status }}{{ doc.trashed_at }}{{ doc.current_path }} +
+
+ +
+
+ +
+
+
+
+ {% else %} +

Trash is empty.

+ {% endif %} +
+
+
+ + + +