show next_at()

This commit is contained in:
James Turk 2025-02-09 15:34:16 -06:00
parent 4980b2e7e5
commit 3279dd9e8c
4 changed files with 70 additions and 48 deletions

View File

@ -31,6 +31,8 @@ def update_generator(
item_id: int, item_id: int,
**kwargs, **kwargs,
) -> TaskGenerator: ) -> TaskGenerator:
# replace "val" with JSON
if "val" in kwargs:
config = {"val": kwargs.pop("val")} config = {"val": kwargs.pop("val")}
kwargs["config"] = json.dumps(config) kwargs["config"] = json.dumps(config)
with db.atomic(): with db.atomic():

View File

@ -1,4 +1,5 @@
from datetime import datetime, timedelta import json
from datetime import date, timedelta, datetime
from enum import Enum from enum import Enum
from peewee import ( from peewee import (
BooleanField, BooleanField,
@ -46,7 +47,6 @@ class Category(BaseModel):
class Task(BaseModel): class Task(BaseModel):
# TODO: rename name
text = TextField() text = TextField()
status = CharField( status = CharField(
choices=[(status.value, status.name) for status in TaskStatus], choices=[(status.value, status.name) for status in TaskStatus],
@ -81,32 +81,51 @@ class TaskGenerator(BaseModel):
last_generated_at = DateTimeField(null=True) last_generated_at = DateTimeField(null=True)
created_at = DateTimeField(default=datetime.now) created_at = DateTimeField(default=datetime.now)
def should_generate(self) -> bool: def next_at(self) -> datetime.date:
if self.deleted: if self.deleted:
return False return None
today = date.today()
val = int(json.loads(self.config)["val"])
if self.type == GeneratorType.DAYS_BETWEEN.value:
if not self.last_generated_at: if not self.last_generated_at:
return True return today
return self.last_generated_at + timedelta(days=val)
elif self.type == GeneratorType.MONTHLY.value:
year, month, day = today.year, today.month, today.day
day_of_month = val
now = datetime.now() if day_of_month < day:
if self.type == GeneratorType.DAYS_BETWEEN: month += 1
days_between = self.config["val"] if month == 13:
days_since = (now - self.last_generated_at).days month = 1
return days_since >= days_between year += 1
elif self.type == GeneratorType.MONTHLY: if day_of_month >= 29 and month == 2:
day_of_month = self.config["val"] maybe_next = date(year, month, 28)
else:
maybe_next = date(year, month, day_of_month)
# check each day until now to see if target day occurred if not self.last_generated_at or self.last_generated_at < maybe_next:
one_day = timedelta(days=1) return maybe_next
check_date = self.last_generated_at + one_day
while check_date <= now:
if check_date.day == day_of_month:
return True
check_date += one_day
return False
# need to go forward one more month
month += 1
if month == 13:
month = 1
year += 1
if day_of_month >= 29 and month == 2:
maybe_next = date(year, month, 28)
else:
maybe_next = date(year, month, day_of_month)
return maybe_next
def should_generate(self) -> bool:
next = self.next_at()
if not next:
return False return False
return next <= date.today()
def initialize_db(): def initialize_db():

View File

@ -42,17 +42,19 @@ class TableColumnConfig:
display_name: str, display_name: str,
*, *,
default=None, default=None,
enable_editor=False,
enum=None, enum=None,
filterable=True,
preprocessor=None, preprocessor=None,
enable_editor=False,
filterable=True,
read_only=False,
): ):
self.field = field self.field = field
self.display_name = display_name self.display_name = display_name
self.default = default self.default = default
self.enable_editor = enable_editor
self.enum = enum self.enum = enum
self.enable_editor = enable_editor
self.filterable = filterable self.filterable = filterable
self.read_only = read_only
if preprocessor: if preprocessor:
self.preprocessor = preprocessor self.preprocessor = preprocessor
elif self.enum: elif self.enum:
@ -223,6 +225,8 @@ class TableEditor(App):
# prepopulate with either the field default or the current filter # prepopulate with either the field default or the current filter
prepopulated = {} prepopulated = {}
for fc in self.TABLE_CONFIG: for fc in self.TABLE_CONFIG:
if fc.read_only:
continue
val = self.filters.get(fc.field, fc.default) val = self.filters.get(fc.field, fc.default)
if val is not None: if val is not None:
# enums use comma separated filters # enums use comma separated filters
@ -320,16 +324,21 @@ class TableEditor(App):
self.saved_cursor_pos = (self.table.cursor_row, self.table.cursor_column) self.saved_cursor_pos = (self.table.cursor_row, self.table.cursor_column)
def action_start_change(self): def action_start_change(self):
# invalid position
if self.table.cursor_row is None or self.table.cursor_column == 0: if self.table.cursor_row is None or self.table.cursor_column == 0:
return return
# check if editable
cconf = self._active_column_config()
if cconf.read_only:
return
self._save_cursor() self._save_cursor()
current_value = self.table.get_cell_at( current_value = self.table.get_cell_at(
(self.table.cursor_row, self.table.cursor_column) (self.table.cursor_row, self.table.cursor_column)
) )
if current_value.endswith(ELLIPSIS): if current_value.endswith(ELLIPSIS):
# TODO: flash message? self.notify("multi-line text, use (e)dit")
# need to edit with e
return return
current_value = remove_rich_tag(current_value) current_value = remove_rich_tag(current_value)
self._show_input("edit", current_value) self._show_input("edit", current_value)

View File

@ -1,5 +1,4 @@
import json import json
from datetime import datetime
from ..controller.generators import ( from ..controller.generators import (
get_generator, get_generator,
@ -11,17 +10,9 @@ from ..db import GeneratorType
from .editor import ( from .editor import (
TableEditor, TableEditor,
TableColumnConfig, TableColumnConfig,
NotifyValidationError,
) )
def due_preprocessor(val):
try:
return datetime.strptime(val, "%Y-%m-%d")
except ValueError:
raise NotifyValidationError("Invalid date format. Use YYYY-MM-DD")
class TaskGenEditor(TableEditor): class TaskGenEditor(TableEditor):
TABLE_CONFIG = ( TABLE_CONFIG = (
TableColumnConfig("id", "ID"), TableColumnConfig("id", "ID"),
@ -32,7 +23,8 @@ class TaskGenEditor(TableEditor):
default=GeneratorType.DAYS_BETWEEN.value, default=GeneratorType.DAYS_BETWEEN.value,
enum=GeneratorType, enum=GeneratorType,
), ),
TableColumnConfig("val", "Value", default=""), TableColumnConfig("val", "Value", default="1"),
TableColumnConfig("next_at", "Next @", default="", read_only=True),
) )
def __init__(self): def __init__(self):
@ -45,14 +37,14 @@ class TaskGenEditor(TableEditor):
items = get_generators() items = get_generators()
for item in items: for item in items:
self.table.add_row( self.table.add_row(
str(item.id), item.template, item.type, json.loads(item.config)["val"] str(item.id),
item.template,
item.type,
json.loads(item.config)["val"],
str(item.next_at()),
) )
def run(): def run():
app = TaskGenEditor() app = TaskGenEditor()
app.run() app.run()
if __name__ == "__main__":
run()