Ensure that adding a document whilst on an older date page, uses that date as its upload date
All checks were successful
CI / test (push) Successful in 5m43s
Lint / test (push) Successful in 32s
Trivy / test (push) Successful in 23s

This commit is contained in:
Miguel Jacq 2025-12-02 17:27:30 +11:00
parent bae91f56e6
commit 779049e467
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
3 changed files with 116 additions and 21 deletions

View file

@ -1,3 +1,7 @@
# 0.6.2
* Ensure that adding a document whilst on an older date page, uses that date as its upload date
# 0.6.1 # 0.6.1
* Consolidate some code related to opening documents using the Documents feature. * Consolidate some code related to opening documents using the Documents feature.

View file

@ -1333,9 +1333,16 @@ class DBManager:
project_id: int, project_id: int,
file_path: str, file_path: str,
description: str | None = None, description: str | None = None,
uploaded_at: str | None = None,
) -> int: ) -> int:
""" """
Read a file from disk and store it as a BLOB in project_documents. Read a file from disk and store it as a BLOB in project_documents.
Args:
project_id: The project to attach the document to
file_path: Path to the file to upload
description: Optional description
uploaded_at: Optional date in YYYY-MM-DD format. If None, uses current date.
""" """
path = Path(file_path) path = Path(file_path)
if not path.is_file(): if not path.is_file():
@ -1349,22 +1356,43 @@ class DBManager:
with self.conn: with self.conn:
cur = self.conn.cursor() cur = self.conn.cursor()
cur.execute( if uploaded_at is not None:
""" # Use explicit date
INSERT INTO project_documents cur.execute(
(project_id, file_name, mime_type, """
description, size_bytes, data) INSERT INTO project_documents
VALUES (?, ?, ?, ?, ?, ?); (project_id, file_name, mime_type,
""", description, size_bytes, uploaded_at, data)
( VALUES (?, ?, ?, ?, ?, ?, ?);
project_id, """,
file_name, (
mime_type, project_id,
description, file_name,
size_bytes, mime_type,
Binary(data), description,
), size_bytes,
) uploaded_at,
Binary(data),
),
)
else:
# Let DB default to current date
cur.execute(
"""
INSERT INTO project_documents
(project_id, file_name, mime_type,
description, size_bytes, data)
VALUES (?, ?, ?, ?, ?, ?);
""",
(
project_id,
file_name,
mime_type,
description,
size_bytes,
Binary(data),
),
)
doc_id = cur.lastrowid or 0 doc_id = cur.lastrowid or 0
return int(doc_id) return int(doc_id)
@ -1376,6 +1404,20 @@ class DBManager:
(description, doc_id), (description, doc_id),
) )
def update_document_uploaded_at(self, doc_id: int, uploaded_at: str) -> None:
"""
Update the uploaded_at date for a document.
Args:
doc_id: Document ID
uploaded_at: Date in YYYY-MM-DD format
"""
with self.conn:
self.conn.execute(
"UPDATE project_documents SET uploaded_at = ? WHERE id = ?;",
(uploaded_at, doc_id),
)
def delete_document(self, doc_id: int) -> None: def delete_document(self, doc_id: int) -> None:
with self.conn: with self.conn:
self.conn.execute("DELETE FROM project_documents WHERE id = ?;", (doc_id,)) self.conn.execute("DELETE FROM project_documents WHERE id = ?;", (doc_id,))

View file

@ -151,7 +151,7 @@ class TodaysDocumentsWidget(QFrame):
def _open_documents_dialog(self) -> None: def _open_documents_dialog(self) -> None:
"""Open the full DocumentsDialog.""" """Open the full DocumentsDialog."""
dlg = DocumentsDialog(self._db, self) dlg = DocumentsDialog(self._db, self, current_date=self._current_date)
dlg.exec() dlg.exec()
# Refresh after any changes # Refresh after any changes
self.reload() self.reload()
@ -179,12 +179,14 @@ class DocumentsDialog(QDialog):
db: DBManager, db: DBManager,
parent: QWidget | None = None, parent: QWidget | None = None,
initial_project_id: Optional[int] = None, initial_project_id: Optional[int] = None,
current_date: Optional[str] = None,
) -> None: ) -> None:
super().__init__(parent) super().__init__(parent)
self._db = db self._db = db
self.cfg = load_db_config() self.cfg = load_db_config()
self._reloading_docs = False self._reloading_docs = False
self._search_text: str = "" self._search_text: str = ""
self._current_date = current_date # Store the current date for document uploads
self.setWindowTitle(strings._("project_documents_title")) self.setWindowTitle(strings._("project_documents_title"))
self.resize(900, 450) self.resize(900, 450)
@ -382,10 +384,9 @@ class DocumentsDialog(QDialog):
desc_item = QTableWidgetItem(description or "") desc_item = QTableWidgetItem(description or "")
self.table.setItem(row_idx, self.DESC_COL, desc_item) self.table.setItem(row_idx, self.DESC_COL, desc_item)
# Col 3: Added at (not editable) # Col 3: Added at (editable)
added_label = uploaded_at added_label = uploaded_at
added_item = QTableWidgetItem(added_label) added_item = QTableWidgetItem(added_label)
added_item.setFlags(added_item.flags() & ~Qt.ItemIsEditable)
self.table.setItem(row_idx, self.ADDED_COL, added_item) self.table.setItem(row_idx, self.ADDED_COL, added_item)
# Col 4: Size (not editable) # Col 4: Size (not editable)
@ -422,7 +423,9 @@ class DocumentsDialog(QDialog):
for path in paths: for path in paths:
try: try:
self._db.add_document_from_path(proj_id, path) self._db.add_document_from_path(
proj_id, path, uploaded_at=self._current_date
)
except Exception as e: # pragma: no cover except Exception as e: # pragma: no cover
QMessageBox.warning( QMessageBox.warning(
self, self,
@ -469,7 +472,7 @@ class DocumentsDialog(QDialog):
def _on_item_changed(self, item: QTableWidgetItem) -> None: def _on_item_changed(self, item: QTableWidgetItem) -> None:
""" """
Handle inline edits to Description and Tags. Handle inline edits to Description, Tags, and Added date.
""" """
if self._reloading_docs or item is None: if self._reloading_docs or item is None:
return return
@ -524,9 +527,55 @@ class DocumentsDialog(QDialog):
item.setForeground(QColor()) item.setForeground(QColor())
finally: finally:
self._reloading_docs = False self._reloading_docs = False
return
# Added date column
if col == self.ADDED_COL:
date_str = item.text().strip()
# Validate date format (YYYY-MM-DD)
if not self._validate_date_format(date_str):
QMessageBox.warning(
self,
strings._("project_documents_title"),
(
strings._("documents_invalid_date_format")
if hasattr(strings, "_")
and callable(getattr(strings, "_"))
and "documents_invalid_date_format" in dir(strings)
else f"Invalid date format. Please use YYYY-MM-DD format.\nExample: {date_str[:4]}-01-15"
),
)
# Reload to reset the cell to its original value
self._reload_documents()
return
# Update the database
self._db.update_document_uploaded_at(doc_id, date_str)
return
# --- utils ------------------------------------------------------------- # --- utils -------------------------------------------------------------
def _validate_date_format(self, date_str: str) -> bool:
"""
Validate that a date string is in YYYY-MM-DD format.
Returns True if valid, False otherwise.
"""
import re
from datetime import datetime
# Check basic format with regex
if not re.match(r"^\d{4}-\d{2}-\d{2}$", date_str):
return False
# Validate it's a real date
try:
datetime.strptime(date_str, "%Y-%m-%d")
return True
except ValueError:
return False
def _open_document(self, doc_id: int, file_name: str) -> None: def _open_document(self, doc_id: int, file_name: str) -> None:
""" """
Fetch BLOB from DB, write to a temporary file, and open with default app. Fetch BLOB from DB, write to a temporary file, and open with default app.