feat: Add filter presets, persistent column widths, and enhanced export options
This commit is contained in:
@@ -487,3 +487,19 @@ Primary action buttons show their keyboard shortcuts in the button text (e.g., "
|
|||||||
|
|
||||||
*This document was generated by the documentation consolidation system.*
|
*This document was generated by the documentation consolidation system.*
|
||||||
*Last updated: 2025-08-05 14:53:36*
|
*Last updated: 2025-08-05 14:53:36*
|
||||||
|
|
||||||
|
## New in v1.14.9: Filters, columns, and exports
|
||||||
|
|
||||||
|
### Filter presets (Save/Load/Delete)
|
||||||
|
- Open the Search/Filter panel (Ctrl+F), set filters, then click Save to store a named preset.
|
||||||
|
- A themed modal dialog asks for a name and shows if you’ll overwrite an existing preset.
|
||||||
|
- Load via the presets dropdown → Load. Delete via Delete.
|
||||||
|
- Presets persist across restarts.
|
||||||
|
|
||||||
|
### Persistent column widths and sort
|
||||||
|
- Resize columns; widths are saved automatically and restored next run.
|
||||||
|
- Click a header to sort; the last sorted column and direction are remembered and re-applied on refresh/startup.
|
||||||
|
|
||||||
|
### Export current (filtered) data
|
||||||
|
- In Export (Ctrl+E), choose scope: All data or Current filtered view.
|
||||||
|
- Works with CSV, JSON, XML, and PDF exporters.
|
||||||
|
|||||||
@@ -209,6 +209,11 @@ Powerful data filtering and search capabilities for analyzing your health data.
|
|||||||
- Filter to last 30 days with depression scores between 3-6
|
- Filter to last 30 days with depression scores between 3-6
|
||||||
- Combine filters: High anxiety + specific medicine + date range
|
- Combine filters: High anxiety + specific medicine + date range
|
||||||
|
|
||||||
|
#### Presets and Persistence (v1.14.9)
|
||||||
|
- Save/Load/Delete filter presets directly from the Search/Filter panel. Presets are named and persist across restarts. Save dialog is themed and shows overwrite/new hints.
|
||||||
|
- Column widths and last sorted column/direction are remembered. Resizing headers or sorting stores preferences; they’re re-applied on refresh/startup.
|
||||||
|
- Export can target the current filtered view: choose in the Export window to export only matching rows (CSV/JSON/XML/PDF).
|
||||||
|
|
||||||
### 📝 Data Management
|
### 📝 Data Management
|
||||||
Robust data handling with comprehensive backup and migration support.
|
Robust data handling with comprehensive backup and migration support.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
"""Tests for persistence features: column widths and last sort reapplication."""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.ui_manager import UIManager
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def root_window():
|
||||||
|
root = tk.Tk()
|
||||||
|
yield root
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ui_manager(root_window, mock_logger):
|
||||||
|
return UIManager(root_window, mock_logger)
|
||||||
|
|
||||||
|
|
||||||
|
def test_table_applies_saved_column_widths(ui_manager, root_window, monkeypatch):
|
||||||
|
# Provide a fake get_pref that returns widths for some columns
|
||||||
|
saved = {"column_widths": {"Date": 123, "Note": 456}}
|
||||||
|
|
||||||
|
def fake_get_pref(key, default=None): # type: ignore[override]
|
||||||
|
return saved.get(key, default)
|
||||||
|
|
||||||
|
monkeypatch.setattr("src.ui_manager.get_pref", fake_get_pref)
|
||||||
|
|
||||||
|
main = ttk.Frame(root_window)
|
||||||
|
table_ui = ui_manager.create_table_frame(main)
|
||||||
|
tree: ttk.Treeview = table_ui["tree"]
|
||||||
|
|
||||||
|
# Verify widths applied
|
||||||
|
assert int(tree.column("Date", option="width")) == 123
|
||||||
|
assert int(tree.column("Note", option="width")) == 456
|
||||||
|
|
||||||
|
|
||||||
|
def test_reapply_last_sort_descending(ui_manager, root_window, monkeypatch):
|
||||||
|
# Simulate last sort on 'Date' descending
|
||||||
|
saved = {"last_sort": {"column": "Date", "ascending": False}}
|
||||||
|
|
||||||
|
def fake_get_pref(key, default=None): # type: ignore[override]
|
||||||
|
return saved.get(key, default)
|
||||||
|
|
||||||
|
monkeypatch.setattr("src.ui_manager.get_pref", fake_get_pref)
|
||||||
|
|
||||||
|
main = ttk.Frame(root_window)
|
||||||
|
table_ui = ui_manager.create_table_frame(main)
|
||||||
|
tree: ttk.Treeview = table_ui["tree"]
|
||||||
|
|
||||||
|
# Insert a few rows with Date values that sort numerically
|
||||||
|
# Columns are dynamic; ensure we provide a value for each column
|
||||||
|
cols = list(tree["columns"])
|
||||||
|
idx_date = cols.index("Date")
|
||||||
|
|
||||||
|
def row_with_date(val: str):
|
||||||
|
row = [""] * len(cols)
|
||||||
|
row[idx_date] = val
|
||||||
|
return row
|
||||||
|
|
||||||
|
tree.insert("", "end", values=row_with_date("1"))
|
||||||
|
tree.insert("", "end", values=row_with_date("3"))
|
||||||
|
tree.insert("", "end", values=row_with_date("2"))
|
||||||
|
|
||||||
|
# Reapply last sort (descending) and verify first row has Date '3'
|
||||||
|
ui_manager.reapply_last_sort(tree)
|
||||||
|
first_item = tree.get_children("")[0]
|
||||||
|
first_vals = tree.item(first_item, "values")
|
||||||
|
assert str(first_vals[idx_date]) == "3"
|
||||||
Reference in New Issue
Block a user