From f8909d7fcb6a5d61bdd2d818bc3e8fb772ff5ccc Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Wed, 3 Dec 2025 14:59:57 +1100 Subject: [PATCH] Add 'Created at' to time log table. Show total hours for the day in the time log table (not just in the widget in sidebar) --- CHANGELOG.md | 2 ++ bouquin/db.py | 4 +++- bouquin/locales/en.json | 1 + bouquin/time_log.py | 34 ++++++++++++++++++++++++---------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebc3891..46c3d9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # 0.6.2 * Ensure that adding a document whilst on an older date page, uses that date as its upload date + * Add 'Created at' to time log table. + * Show total hours for the day in the time log table (not just in the widget in sidebar) # 0.6.1 diff --git a/bouquin/db.py b/bouquin/db.py index ea8ba5c..9baad95 100644 --- a/bouquin/db.py +++ b/bouquin/db.py @@ -1075,7 +1075,8 @@ class DBManager: t.activity_id, a.name AS activity_name, t.minutes, - t.note + t.note, + t.created_at AS created_at FROM time_log t JOIN projects p ON p.id = t.project_id JOIN activities a ON a.id = t.activity_id @@ -1097,6 +1098,7 @@ class DBManager: r["activity_name"], r["minutes"], r["note"], + r["created_at"], ) ) return result diff --git a/bouquin/locales/en.json b/bouquin/locales/en.json index 13fe64c..8d0a04f 100644 --- a/bouquin/locales/en.json +++ b/bouquin/locales/en.json @@ -206,6 +206,7 @@ "delete_time_entry": "Delete time entry", "group_by": "Group by", "hours": "Hours", + "created_at": "Created at", "invalid_activity_message": "The activity is invalid", "invalid_activity_title": "Invalid activity", "invalid_project_message": "The project is invalid", diff --git a/bouquin/time_log.py b/bouquin/time_log.py index d4170ac..2afbea8 100644 --- a/bouquin/time_log.py +++ b/bouquin/time_log.py @@ -4,8 +4,9 @@ import csv import html from collections import defaultdict -from typing import Optional +from datetime import datetime from sqlcipher3.dbapi2 import IntegrityError +from typing import Optional from PySide6.QtCore import Qt, QDate, QUrl from PySide6.QtGui import QPainter, QColor, QImage, QTextDocument, QPageLayout @@ -204,12 +205,6 @@ class TimeLogWidget(QFrame): class TimeLogDialog(QDialog): """ Per-day time log dialog. - - Lets you: - 1) choose a project - 2) enter an activity (free-text with autocomplete) - 3) enter time in decimal hours (0.25 = 15 min, 0.17 ≈ 10 min) - 4) manage entries for this date """ def __init__( @@ -230,6 +225,8 @@ class TimeLogDialog(QDialog): # programmatic item changes as user edits. self._reloading_entries: bool = False + self.total_hours = 0 + self.close_after_add = close_after_add self.setWindowTitle(strings._("time_log_for").format(date=date_iso)) @@ -245,7 +242,6 @@ class TimeLogDialog(QDialog): date_row.addStretch(1) - # You can i18n this later if you like self.change_date_btn = QPushButton(strings._("time_log_change_date")) self.change_date_btn.clicked.connect(self._on_change_date_clicked) date_row.addWidget(self.change_date_btn) @@ -309,13 +305,14 @@ class TimeLogDialog(QDialog): # --- Table of entries for this date self.table = QTableWidget() - self.table.setColumnCount(4) + self.table.setColumnCount(5) self.table.setHorizontalHeaderLabels( [ strings._("project"), strings._("activity"), strings._("note"), strings._("hours"), + strings._("created_at"), ] ) self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) @@ -324,6 +321,7 @@ class TimeLogDialog(QDialog): self.table.horizontalHeader().setSectionResizeMode( 3, QHeaderView.ResizeToContents ) + self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.itemSelectionChanged.connect(self._on_row_selected) @@ -331,8 +329,12 @@ class TimeLogDialog(QDialog): self.table.itemChanged.connect(self._on_table_item_changed) root.addWidget(self.table, 1) - # --- Close button + # --- Total time and Close button close_row = QHBoxLayout() + self.total_label = QLabel( + strings._("time_log_total_hours").format(hours=self.total_hours) + ) + close_row.addWidget(self.total_label) close_row.addStretch(1) close_btn = QPushButton(strings._("close")) close_btn.clicked.connect(self.accept) @@ -381,11 +383,16 @@ class TimeLogDialog(QDialog): note = r[7] or "" minutes = r[6] hours = minutes / 60.0 + created_at = r[8] + ca_utc = datetime.fromisoformat(created_at.replace("Z", "+00:00")) + ca_local = ca_utc.astimezone() + created = f"{ca_local.day} {ca_local.strftime('%b %Y, %H:%M%p')}" item_proj = QTableWidgetItem(project_name) item_act = QTableWidgetItem(activity_name) item_note = QTableWidgetItem(note) item_hours = QTableWidgetItem(f"{hours:.2f}") + item_created_at = QTableWidgetItem(created) # store the entry id on the first column item_proj.setData(Qt.ItemDataRole.UserRole, entry_id) @@ -394,9 +401,16 @@ class TimeLogDialog(QDialog): self.table.setItem(row_idx, 1, item_act) self.table.setItem(row_idx, 2, item_note) self.table.setItem(row_idx, 3, item_hours) + self.table.setItem(row_idx, 4, item_created_at) finally: self._reloading_entries = False + total_minutes = sum(r[6] for r in rows) + self.total_hours = total_minutes / 60.0 + self.total_label.setText( + strings._("time_log_total_hours").format(hours=self.total_hours) + ) + self._current_entry_id = None self.delete_btn.setEnabled(False) self.add_update_btn.setText("&" + strings._("add_time_entry"))