158 lines
4.1 KiB
Python
158 lines
4.1 KiB
Python
import logging
|
|
import os
|
|
import sys
|
|
from datetime import date, datetime, timedelta
|
|
from random import randint
|
|
|
|
from caldav import DAVClient
|
|
from dotenv import find_dotenv, load_dotenv
|
|
|
|
from printer import p
|
|
|
|
logging.basicConfig(
|
|
format="{asctime} {levelname:8} {name}: {message}",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
style="{",
|
|
stream=sys.stdout,
|
|
force=True,
|
|
)
|
|
|
|
log = logging.getLogger("todo-receipt")
|
|
log.setLevel(logging.DEBUG)
|
|
|
|
if load_dotenv(find_dotenv(usecwd=True)):
|
|
log.debug("Loaded .env")
|
|
else:
|
|
log.debug("Didn't find .env")
|
|
|
|
CALDAV_URL = os.getenv("CALDAV_URL")
|
|
CALDAV_USERNAME = os.getenv("CALDAV_USERNAME")
|
|
CALDAV_PASSWORD = os.getenv("CALDAV_PASSWORD")
|
|
|
|
day = timedelta(days=1)
|
|
week = timedelta(days=7)
|
|
today = date.today()
|
|
yesterday = today - day
|
|
tomorrow = today + day
|
|
next_week = today + week
|
|
|
|
|
|
def coerce_date(datetime):
|
|
try:
|
|
return datetime.date()
|
|
except:
|
|
return datetime
|
|
|
|
|
|
def shuffle_with_priority(tasks):
|
|
return sorted(
|
|
tasks,
|
|
key=lambda task: (
|
|
task.icalendar_component.get("priority", 5),
|
|
randint(0, 10),
|
|
),
|
|
)
|
|
|
|
|
|
with DAVClient(
|
|
url=CALDAV_URL, username=CALDAV_USERNAME, password=CALDAV_PASSWORD
|
|
) as client:
|
|
log.debug("Loading calendars")
|
|
principal = client.principal()
|
|
calendars = principal.calendars()
|
|
|
|
events = []
|
|
todos = []
|
|
|
|
for c in calendars:
|
|
log.debug(f"Fetching from {c.name}")
|
|
if c.name not in ["Schedule"]:
|
|
log.debug("Fetching events and todos")
|
|
events.extend(c.events())
|
|
todos.extend(c.todos())
|
|
else:
|
|
log.debug("Finding cancelled classes")
|
|
for event in c.events():
|
|
if event.icalendar_component.get("status") == "CANCELED":
|
|
log.debug(
|
|
f"Found cancelled class {event.icalendar_component.get('summary')}"
|
|
)
|
|
events.append(event)
|
|
|
|
must_do = []
|
|
should_do = []
|
|
want_to_do = []
|
|
|
|
log.debug("Processing todos")
|
|
for task in todos:
|
|
due = task.get_due()
|
|
if due is None:
|
|
want_to_do.append(task)
|
|
continue
|
|
|
|
task.expand_rrule(yesterday, next_week)
|
|
due = coerce_date(due)
|
|
|
|
try:
|
|
start = coerce_date(task.icalendar_component.get("dtstart").dt or yesterday)
|
|
except:
|
|
start = yesterday
|
|
|
|
if today < start:
|
|
continue
|
|
|
|
if due < tomorrow:
|
|
must_do.append(task)
|
|
elif due < next_week:
|
|
should_do.append(task)
|
|
else:
|
|
want_to_do.append(task)
|
|
|
|
scheduled = []
|
|
|
|
log.debug("Processing events")
|
|
for event in events:
|
|
event.expand_rrule(yesterday, next_week)
|
|
start_day = coerce_date(event.icalendar_component.get("dtstart").dt)
|
|
end_day = coerce_date(event.icalendar_component.get("dtend").dt)
|
|
|
|
if end_day < today or today < start_day:
|
|
continue
|
|
else:
|
|
scheduled.append(event)
|
|
|
|
should_want = shuffle_with_priority(should_do) + shuffle_with_priority(want_to_do)
|
|
|
|
categories = {
|
|
"Must do": must_do,
|
|
"Should do": should_want[:3],
|
|
"Want to do": should_want[3:6],
|
|
}
|
|
if len(should_want) > 6:
|
|
categories["Leftover"] = should_want[7:]
|
|
|
|
with p:
|
|
log.debug("Printing output")
|
|
p.title(" TODO List ", size=4)
|
|
p.subtitle(datetime.today().strftime("%Y-%m-%d"))
|
|
p.ln()
|
|
|
|
if scheduled:
|
|
p.textln("Scheduled:")
|
|
for event in scheduled:
|
|
summary = event.icalendar_component["summary"]
|
|
if event.icalendar_component.get("status") == "CANCELED":
|
|
summary += " (CANCELED)"
|
|
start = event.icalendar_component.get("dtstart").dt
|
|
p.textln(f"[ ] {summary} @ {start.strftime('%H:%M')}")
|
|
else:
|
|
p.textln("Scheduled: (None)")
|
|
|
|
for k, v in categories.items():
|
|
p.ln()
|
|
if v:
|
|
p.textln(f"{k}:")
|
|
for task in v:
|
|
p.textln(f"[ ] {task.icalendar_component['summary']}")
|
|
else:
|
|
p.textln(f"{k}: (None)")
|