134 lines
3.5 KiB
Python
134 lines
3.5 KiB
Python
import json
|
|
from datetime import date, timedelta, datetime
|
|
from enum import Enum
|
|
from peewee import (
|
|
BooleanField,
|
|
CharField,
|
|
DateTimeField,
|
|
ForeignKeyField,
|
|
Model,
|
|
SqliteDatabase,
|
|
TextField,
|
|
)
|
|
|
|
db = SqliteDatabase(
|
|
"tasks.db",
|
|
pragmas={
|
|
"journal_mode": "wal",
|
|
"synchronous": "normal",
|
|
"foreign_keys": 1,
|
|
},
|
|
)
|
|
|
|
|
|
class TaskStatus(Enum):
|
|
# order is used for progression in toggle
|
|
ZERO = "zero"
|
|
WIP = "wip"
|
|
BLOCKED = "blocked"
|
|
DONE = "done"
|
|
|
|
|
|
class GeneratorType(Enum):
|
|
DAYS_BETWEEN = "days-btwn"
|
|
MONTHLY = "monthly"
|
|
|
|
|
|
class BaseModel(Model):
|
|
class Meta:
|
|
database = db
|
|
|
|
|
|
class Category(BaseModel):
|
|
name = CharField(unique=True)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Task(BaseModel):
|
|
text = TextField()
|
|
status = CharField(
|
|
choices=[(status.value, status.name) for status in TaskStatus],
|
|
default=TaskStatus.ZERO.value,
|
|
)
|
|
due = DateTimeField(null=True)
|
|
category = ForeignKeyField(Category, backref="tasks", null=True)
|
|
type = CharField()
|
|
created_at = DateTimeField(default=datetime.now)
|
|
updated_at = DateTimeField(default=datetime.now)
|
|
deleted = BooleanField(default=False)
|
|
|
|
def save(self, *args, **kwargs):
|
|
self.updated_at = datetime.now()
|
|
return super(Task, self).save(*args, **kwargs)
|
|
|
|
|
|
class SavedSearch(BaseModel):
|
|
name = CharField(unique=True)
|
|
filters = CharField()
|
|
sort_string = CharField()
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class TaskGenerator(BaseModel):
|
|
template = CharField()
|
|
type = CharField()
|
|
config = TextField() # JSON
|
|
deleted = BooleanField(default=False)
|
|
last_generated_at = DateTimeField(null=True)
|
|
created_at = DateTimeField(default=datetime.now)
|
|
|
|
def next_at(self) -> datetime.date:
|
|
if self.deleted:
|
|
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:
|
|
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
|
|
|
|
# if we need to go forward a month
|
|
if day_of_month < day:
|
|
month += 1
|
|
if month == 13:
|
|
month = 1
|
|
year += 1
|
|
|
|
# recurring tasks on 29-31 in Feb will just happen on 28th
|
|
if day_of_month >= 29 and month == 2:
|
|
maybe_next = date(year, month, 28)
|
|
else:
|
|
maybe_next = date(year, month, day_of_month)
|
|
|
|
if not self.last_generated_at or self.last_generated_at < maybe_next:
|
|
return maybe_next
|
|
|
|
# TODO: this doesn't handle if a month was missed somehow, just advances one
|
|
# same logic as above, if we're stepping another month forward
|
|
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 initialize_db():
|
|
db.connect()
|
|
db.create_tables([Category, Task, SavedSearch, TaskGenerator])
|
|
if not Category.select().exists():
|
|
Category.create(name="default")
|
|
db.close()
|