New job: daily tasks from a calendar
This commit is contained in:
parent
b9e96b5945
commit
618b1641ff
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
__pycache__
|
||||
calendar_url.txt
|
||||
17
README.md
17
README.md
@ -14,6 +14,23 @@ pip install python-escpos[all] --user
|
||||
pip install chess
|
||||
```
|
||||
|
||||
### Tasks from Google Calendar
|
||||
|
||||
```
|
||||
pip install requests icalendar recurring-ical-events pytz
|
||||
```
|
||||
|
||||
You need to specify the calendar URL by pasting it into `calendar_url.txt` in this project directory.
|
||||
|
||||
> To find the secret iCal URL for your Google Calendar, follow these steps:
|
||||
>
|
||||
> Open Google Calendar in your web browser.
|
||||
> In the top right, click the Settings (gear icon) > Settings.
|
||||
> On the left sidebar, under "Settings for my calendars", click the name of the calendar you want to use.
|
||||
> Scroll down to the "Integrate calendar" section.
|
||||
> Look for the field "Secret address in iCal format".
|
||||
> Copy this URL (it should end in .ics).
|
||||
|
||||
## Config
|
||||
|
||||
|
||||
|
||||
108
jobs/tasks.py
Normal file
108
jobs/tasks.py
Normal file
@ -0,0 +1,108 @@
|
||||
import datetime
|
||||
import os.path
|
||||
from .base import Job
|
||||
|
||||
# Wrap imports to prevent crashing if dependencies are missing
|
||||
try:
|
||||
import requests
|
||||
import icalendar
|
||||
import recurring_ical_events
|
||||
import pytz
|
||||
HAS_ICAL_DEPS = True
|
||||
except ImportError:
|
||||
HAS_ICAL_DEPS = False
|
||||
|
||||
class TasksJob(Job):
|
||||
def get_name(self):
|
||||
return "UKOLY DNE"
|
||||
|
||||
def configure(self):
|
||||
print("\n--- Configure Calendar URL ---")
|
||||
print("To find your Google Calendar URL:")
|
||||
print("1. Go to Settings > Select Calendar > Integrate calendar")
|
||||
print("2. Copy 'Secret address in iCal format'")
|
||||
print("-" * 30)
|
||||
print("Enter the public or secret address in iCal format (.ics).")
|
||||
print("Example: https://calendar.google.com/calendar/ical/.../basic.ics")
|
||||
url = input("URL: ").strip()
|
||||
if url:
|
||||
with open('calendar_url.txt', 'w') as f:
|
||||
f.write(url)
|
||||
print("URL saved.")
|
||||
|
||||
def print_body(self, p):
|
||||
if not HAS_ICAL_DEPS:
|
||||
print("Error: Missing iCal Libraries.")
|
||||
p.text("Error: Missing iCal Libraries.\n")
|
||||
p.text("Run: pip install requests icalendar recurring-ical-events pytz\n")
|
||||
return
|
||||
|
||||
if not os.path.exists('calendar_url.txt'):
|
||||
print("Error: Calendar URL not configured.")
|
||||
p.text("Error: Calendar URL not configured.\n")
|
||||
p.text("Run configuration in TUI first.\n")
|
||||
return
|
||||
|
||||
with open('calendar_url.txt', 'r') as f:
|
||||
url = f.read().strip()
|
||||
|
||||
try:
|
||||
# 1. Fetch the .ics file
|
||||
# Timeout is important so the printer doesn't hang forever
|
||||
response = requests.get(url, timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
# 2. Parse Calendar
|
||||
cal = icalendar.Calendar.from_ical(response.content)
|
||||
|
||||
# 3. Calculate Time Range (Today)
|
||||
now = datetime.datetime.now().astimezone()
|
||||
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = now.replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
|
||||
# 4. Expand Recurring Events
|
||||
# This library handles RRULEs (e.g. "Every Monday") automatically
|
||||
events = recurring_ical_events.of(cal).between(start_of_day, end_of_day)
|
||||
|
||||
# 5. Sort by start time
|
||||
# Helper to get sortable time
|
||||
def get_start_time(e):
|
||||
dt = e.get('DTSTART').dt
|
||||
# If it's a date object (all day), convert to datetime for sorting
|
||||
if not isinstance(dt, datetime.datetime):
|
||||
return datetime.datetime.combine(dt, datetime.time.min).replace(tzinfo=now.tzinfo)
|
||||
return dt
|
||||
|
||||
events.sort(key=get_start_time)
|
||||
|
||||
if not events:
|
||||
p.text("Zadne ukoly pro dnesni den.\n")
|
||||
else:
|
||||
for event in events:
|
||||
summary = str(event.get('SUMMARY', '(bez nazvu)'))
|
||||
dtstart = event.get('DTSTART').dt
|
||||
|
||||
# Format time
|
||||
if not isinstance(dtstart, datetime.datetime):
|
||||
time_str = "Cely den"
|
||||
else:
|
||||
# Convert to local system time for display
|
||||
# (If dtstart is timezone aware, astimezone(None) converts to local)
|
||||
local_dt = dtstart.astimezone(now.tzinfo)
|
||||
time_str = local_dt.strftime('%H:%M')
|
||||
|
||||
# Print Entry
|
||||
p.set(bold=True)
|
||||
p.text(f"{time_str} [_]")
|
||||
p.set(bold=False)
|
||||
p.text(f" {summary}\n")
|
||||
|
||||
location = event.get('LOCATION')
|
||||
if location:
|
||||
p.text(f" @ {str(location)}\n")
|
||||
|
||||
p.text("-" * 32 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
p.text(f"Error: {e}\n")
|
||||
@ -11,6 +11,7 @@ from jobs.joke import JokeJob
|
||||
from jobs.maze_multitarget import MazeMultitargetJob
|
||||
from jobs.flush import FlushJob
|
||||
from jobs.word_search import WordSearchJob
|
||||
from jobs.tasks import TasksJob
|
||||
|
||||
# ==========================================
|
||||
# CONFIGURATION
|
||||
@ -60,6 +61,7 @@ JOBS = [
|
||||
MathHomeworkJob(),
|
||||
UnitConversionJob(),
|
||||
ChessPuzzleJob(),
|
||||
TasksJob(),
|
||||
MazeJob(),
|
||||
DivisionCipherJob(),
|
||||
DecimalDivisionJob(),
|
||||
|
||||
30
print_tasks.py
Normal file
30
print_tasks.py
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
from print_server import get_printer
|
||||
from jobs.tasks import TasksJob
|
||||
|
||||
def main():
|
||||
print("Initializing printer...")
|
||||
p = get_printer()
|
||||
if not p:
|
||||
print("Failed to connect to printer.")
|
||||
return
|
||||
|
||||
print("Fetching and printing tasks...")
|
||||
job = TasksJob()
|
||||
|
||||
try:
|
||||
# Run the job
|
||||
job.run(p)
|
||||
print("Done.")
|
||||
except Exception as e:
|
||||
print(f"Error during print job: {e}")
|
||||
finally:
|
||||
# Ensure connection is closed cleanly
|
||||
if hasattr(p, 'close'):
|
||||
time.sleep(0.5)
|
||||
p.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(" == Tasks Printer ==")
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user