mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-18 12:19:18 -05:00
fix(dashboard): remove cache to fix ORM detachment on second visit (Issue #549)
- Dashboard cached template data containing ORM objects (active_timer, recent_entries, top_projects, templates) that became detached when served in a different request, causing 'Instance not bound to a Session' and 'Database Error' on second visit - Add migration 133 to merge heads 132 (timesheet governance) and 129 (task tags) so flask db upgrade runs without conflicts - Update CHANGELOG
This commit is contained in:
@@ -8,10 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- **Dashboard cache (Issue #549)** — Removed dashboard caching that caused "Instance not bound to a Session" and "Database Error" on second visit. Cached template data contained ORM objects (active_timer, recent_entries, top_projects, templates, etc.) that become detached when served in a different request.
|
||||
- **Task description field (Issue #535)** — When creating or editing a task, the description field could appear missing or broken if the Toast UI Editor (loaded from CDN) failed to load (e.g. reverse proxy, CSP, Firefox, or offline). A fallback now shows a plain textarea so users can always enter a description; Markdown is still supported when the rich editor loads.
|
||||
- **ZUGFeRD / PDF/A-3 and PEPPOL (Discussion #433)** — ZUGFeRD embedding no longer silently succeeds without XML when the embed step fails; export is aborted with an actionable error. XMP metadata is created when missing so validators recognize the document. Optional PDF/A-3 normalization (XMP identification and output intent) and optional veraPDF validation gate added. Native PEPPOL transport (SML/SMP + AS4) and strict sender/recipient identifier validation added.
|
||||
|
||||
### Added
|
||||
- **Migration merge 133** — Merge heads 132 (timesheet governance) and 129 (task tags) so `flask db upgrade` runs without conflicts.
|
||||
- **PEPPOL native transport** — Transport mode can be set to **Native** (SML/SMP participant discovery + AS4 send) in addition to **Generic** (HTTP JSON access point). Sender and recipient identifiers are validated before send. New settings: `peppol_transport_mode`, `peppol_sml_url`, `peppol_native_cert_path`, `peppol_native_key_path` (Admin → Peppol e-Invoicing).
|
||||
- **PDF/A-3 and validation** — Option **Normalize ZUGFeRD PDFs to PDF/A-3** and optional **Run veraPDF after export** with configurable path. Migration `130_add_peppol_transport_mode_and_native` adds the new columns.
|
||||
- **Dashboard timer widget** — Pause and Stop buttons while a timer is running (Pause saves the segment so you can resume later). When no timer is active, a prominent "Resume (project name)" button restarts tracking with the same project/task/notes as your last entry. Quick time adjustment buttons (−15 / −5 / +5 / +15 minutes) let you correct the current session without leaving the dashboard. New route `POST /timer/adjust` for start-time adjustment.
|
||||
|
||||
+4
-16
@@ -25,19 +25,10 @@ def dashboard():
|
||||
# Update user segments periodically (cached, not every request)
|
||||
update_user_segments_if_needed(current_user.id, current_user)
|
||||
|
||||
# Use caching for dashboard data (5 minute TTL)
|
||||
# Skip cache when testing: cached data can contain ORM objects that become detached
|
||||
# when served in a different request, causing "Instance not bound to a Session" errors.
|
||||
from app.utils.cache import get_cache, cached
|
||||
|
||||
cache = get_cache()
|
||||
cache_key = f"dashboard:{current_user.id}"
|
||||
use_cache = not current_app.testing
|
||||
|
||||
if use_cache:
|
||||
cached_data = cache.get(cache_key)
|
||||
if cached_data:
|
||||
return render_template("main/dashboard.html", **cached_data)
|
||||
# Do not cache dashboard template_data: it contains ORM objects (active_timer,
|
||||
# recent_entries, top_projects, templates, etc.) that become detached when
|
||||
# served in a different request, causing "Instance not bound to a Session"
|
||||
# and "Database Error" on second visit (Issue #549).
|
||||
|
||||
# Get user's active timer
|
||||
active_timer = current_user.active_timer
|
||||
@@ -161,9 +152,6 @@ def dashboard():
|
||||
"total_hours": total_hours, # For donation widget
|
||||
}
|
||||
|
||||
if use_cache:
|
||||
cache.set(cache_key, template_data, ttl=300)
|
||||
|
||||
return render_template("main/dashboard.html", **template_data)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Merge heads 132_add_timesheet_governance_and_time_off and 129_add_task_tags
|
||||
|
||||
Revision ID: 133_merge_132_129_heads
|
||||
Revises: 132_add_timesheet_governance_and_time_off, 129_add_task_tags
|
||||
Create Date: 2026-03-08
|
||||
|
||||
Resolves multiple heads so 'alembic upgrade head' / 'flask db upgrade' can run.
|
||||
Branch from 117: 118_add_locked_client_id -> 119..128 -> 129_add_task_tags.
|
||||
Branch from 129_merge_118_128_heads: 130 -> 131 -> 132.
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
|
||||
revision = "133_merge_132_129_heads"
|
||||
down_revision = ("132_add_timesheet_governance_and_time_off", "129_add_task_tags")
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""No schema changes - merge only."""
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
"""No schema changes - merge only."""
|
||||
pass
|
||||
Reference in New Issue
Block a user