Add option to automatically move yesterday's unchecked TODOs to today on startup
This commit is contained in:
parent
f7903c2cd9
commit
58f4f0a0b5
8 changed files with 99 additions and 4 deletions
|
|
@ -6,6 +6,7 @@
|
|||
* Represent in the History diff pane when an image was the thing that changed
|
||||
* Support theme choice in settings (light/dark/system)
|
||||
* Add Checkboxes in the editor. Typing 'TODO' at the start of a line will auto-convert into a checkbox.
|
||||
* Add option to automatically move yesterday's unchecked TODOs to today on startup
|
||||
|
||||
# 0.1.9
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class DBConfig:
|
|||
key: str
|
||||
idle_minutes: int = 15 # 0 = never lock
|
||||
theme: str = "system"
|
||||
move_todos: bool = False
|
||||
|
||||
|
||||
class DBManager:
|
||||
|
|
|
|||
|
|
@ -886,5 +886,12 @@ class Editor(QTextEdit):
|
|||
|
||||
def setHtml(self, html: str) -> None:
|
||||
super().setHtml(html)
|
||||
|
||||
doc = self.document()
|
||||
block = doc.firstBlock()
|
||||
while block.isValid():
|
||||
self._style_checkbox_glyph(block) # Apply checkbox styling to each block
|
||||
block = block.next()
|
||||
|
||||
# Ensure anchors adopt the palette color on startup
|
||||
self._retint_anchors_to_palette()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
from pathlib import Path
|
||||
from PySide6.QtCore import (
|
||||
|
|
@ -224,7 +225,8 @@ class MainWindow(QMainWindow):
|
|||
self.editor.textChanged.connect(self._on_text_changed)
|
||||
|
||||
# First load + mark dates in calendar with content
|
||||
self._load_selected_date()
|
||||
if not self._load_yesterday_todos():
|
||||
self._load_selected_date()
|
||||
self._refresh_calendar_marks()
|
||||
|
||||
# Restore window position from settings
|
||||
|
|
@ -469,17 +471,31 @@ class MainWindow(QMainWindow):
|
|||
d = self.calendar.selectedDate()
|
||||
return f"{d.year():04d}-{d.month():02d}-{d.day():02d}"
|
||||
|
||||
def _load_selected_date(self, date_iso=False):
|
||||
def _load_selected_date(self, date_iso=False, extra_data=False):
|
||||
if not date_iso:
|
||||
date_iso = self._current_date_iso()
|
||||
try:
|
||||
text = self.db.get_entry(date_iso)
|
||||
if extra_data:
|
||||
# Wrap extra_data in a <p> tag for HTML rendering
|
||||
extra_data_html = f"<p>{extra_data}</p>"
|
||||
|
||||
# Inject the extra_data before the closing </body></html>
|
||||
modified = re.sub(r"(<\/body><\/html>)", extra_data_html + r"\1", text)
|
||||
text = modified
|
||||
self.editor.setHtml(text)
|
||||
self._dirty = True
|
||||
self._save_date(date_iso, True)
|
||||
|
||||
print("end")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Read Error", str(e))
|
||||
return
|
||||
|
||||
self.editor.blockSignals(True)
|
||||
self.editor.setHtml(text)
|
||||
self.editor.blockSignals(False)
|
||||
|
||||
self._dirty = False
|
||||
# track which date the editor currently represents
|
||||
self._active_date_iso = date_iso
|
||||
|
|
@ -500,6 +516,56 @@ class MainWindow(QMainWindow):
|
|||
today = QDate.currentDate()
|
||||
self.calendar.setSelectedDate(today)
|
||||
|
||||
def _load_yesterday_todos(self):
|
||||
try:
|
||||
if not self.cfg.move_todos:
|
||||
return
|
||||
yesterday_str = QDate.currentDate().addDays(-1).toString("yyyy-MM-dd")
|
||||
text = self.db.get_entry(yesterday_str)
|
||||
unchecked_items = []
|
||||
|
||||
# Regex to match the unchecked checkboxes and their associated text
|
||||
checkbox_pattern = re.compile(
|
||||
r"<span[^>]*>(☐)</span>\s*(.*?)</p>", re.DOTALL
|
||||
)
|
||||
|
||||
# Find unchecked items and store them
|
||||
for match in checkbox_pattern.finditer(text):
|
||||
checkbox = match.group(1) # Either ☐ or ☑
|
||||
item_text = match.group(2).strip() # The text after the checkbox
|
||||
if checkbox == "☐": # If it's an unchecked checkbox (☐)
|
||||
unchecked_items.append("☐ " + item_text) # Store the unchecked item
|
||||
|
||||
# Remove the unchecked items from yesterday's HTML content
|
||||
if unchecked_items:
|
||||
# This regex will find the entire checkbox line and remove it from the HTML content
|
||||
uncheckbox_pattern = re.compile(
|
||||
r"<span[^>]*>☐</span>\s*(.*?)</p>", re.DOTALL
|
||||
)
|
||||
modified_text = re.sub(
|
||||
uncheckbox_pattern, "", text
|
||||
) # Remove the checkbox lines
|
||||
|
||||
# Save the modified HTML back to the database
|
||||
self.db.save_new_version(
|
||||
yesterday_str,
|
||||
modified_text,
|
||||
"Unchecked checkbox items moved to next day",
|
||||
)
|
||||
|
||||
# Join unchecked items into a formatted string
|
||||
unchecked_str = "\n".join(
|
||||
[f"<p>{item}</p>" for item in unchecked_items]
|
||||
)
|
||||
|
||||
# Load the unchecked items into the current editor
|
||||
self._load_selected_date(False, unchecked_str)
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
raise SystemError(e)
|
||||
|
||||
def _on_date_changed(self):
|
||||
"""
|
||||
When the calendar selection changes, save the previous day's note if dirty,
|
||||
|
|
@ -592,6 +658,7 @@ class MainWindow(QMainWindow):
|
|||
self.cfg.key = new_cfg.key
|
||||
self.cfg.idle_minutes = getattr(new_cfg, "idle_minutes", self.cfg.idle_minutes)
|
||||
self.cfg.theme = getattr(new_cfg, "theme", self.cfg.theme)
|
||||
self.cfg.move_todos = getattr(new_cfg, "move_todos", self.cfg.move_todos)
|
||||
|
||||
# Persist once
|
||||
save_db_config(self.cfg)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ def load_db_config() -> DBConfig:
|
|||
key = s.value("db/key", "")
|
||||
idle = s.value("ui/idle_minutes", 15, type=int)
|
||||
theme = s.value("ui/theme", "system", type=str)
|
||||
return DBConfig(path=path, key=key, idle_minutes=idle, theme=theme)
|
||||
move_todos = s.value("ui/move_todos", False, type=bool)
|
||||
return DBConfig(
|
||||
path=path, key=key, idle_minutes=idle, theme=theme, move_todos=move_todos
|
||||
)
|
||||
|
||||
|
||||
def save_db_config(cfg: DBConfig) -> None:
|
||||
|
|
@ -33,3 +36,4 @@ def save_db_config(cfg: DBConfig) -> None:
|
|||
s.setValue("db/key", str(cfg.key))
|
||||
s.setValue("ui/idle_minutes", str(cfg.idle_minutes))
|
||||
s.setValue("ui/theme", str(cfg.theme))
|
||||
s.setValue("ui/move_todos", str(cfg.move_todos))
|
||||
|
|
|
|||
|
|
@ -69,6 +69,19 @@ class SettingsDialog(QDialog):
|
|||
|
||||
form.addRow(theme_group)
|
||||
|
||||
# Add Behaviour
|
||||
behaviour_group = QGroupBox("Behaviour")
|
||||
behaviour_layout = QVBoxLayout(behaviour_group)
|
||||
|
||||
self.move_todos = QCheckBox(
|
||||
"Move yesterday's unchecked TODOs to today on startup"
|
||||
)
|
||||
self.move_todos.setChecked(current_settings.move_todos)
|
||||
self.move_todos.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
behaviour_layout.addWidget(self.move_todos)
|
||||
form.addRow(behaviour_group)
|
||||
|
||||
self.path_edit = QLineEdit(str(self._cfg.path))
|
||||
self.path_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
browse_btn = QPushButton("Browse…")
|
||||
|
|
@ -223,11 +236,13 @@ class SettingsDialog(QDialog):
|
|||
selected_theme = Theme.SYSTEM
|
||||
|
||||
key_to_save = self.key if self.save_key_btn.isChecked() else ""
|
||||
|
||||
self._cfg = DBConfig(
|
||||
path=Path(self.path_edit.text()),
|
||||
key=key_to_save,
|
||||
idle_minutes=self.idle_spin.value(),
|
||||
theme=selected_theme.value,
|
||||
move_todos=self.move_todos.isChecked(),
|
||||
)
|
||||
|
||||
save_db_config(self._cfg)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "bouquin"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
description = "Bouquin is a simple, opinionated notebook application written in Python, PyQt and SQLCipher."
|
||||
authors = ["Miguel Jacq <mig@mig5.net>"]
|
||||
readme = "README.md"
|
||||
|
|
|
|||
0
tests.sh
Normal file → Executable file
0
tests.sh
Normal file → Executable file
Loading…
Add table
Add a link
Reference in a new issue