all repos — mycal @ 242427f49ec7fb59f7079883e186ae1f6e821fb9

private calendar anonymiser

mycal.py (view raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from icalendar.cal import Calendar, Event
import requests
from flask import Flask
from datetime import date, datetime, timedelta, time
import zoneinfo
from dataclasses import dataclass
from danoan.toml_dataclass import TomlDataClassIO
from os import environ
import calendar

@dataclass
class CalendarConfig(TomlDataClassIO):
    file: str = ""
    url: str = ""

@dataclass
class Config(TomlDataClassIO):
    name: str
    timezone: str
    calendar: CalendarConfig

def load_config(file_path):
    with open(file_path, "r") as fr:
        return Config.read(fr)

app = Flask(__name__)

config = load_config(environ.get("CONFIG_FILE", "config.toml"))
tz = zoneinfo.ZoneInfo(config.timezone)

def fetch_calendar(calendar_url):
    return requests.get(calendar_url).content

def read_calendar_file(calendar_file):
    with open(calendar_file, 'rb') as f:
        return f.read()

def get_calendar():
    calendar_config = config.calendar
    if calendar_config.file != "":
        return read_calendar_file(calendar_config.file)
    else:
        if calendar_config.url != "":
            return fetch_calendar(calendar_config.url)
        raise ValueError("Calendar URL not configured.")

def fixup_date(dt):
    if type(dt) == date:
        return datetime.combine(dt, time.min, tzinfo=tz)
    elif dt.tzinfo is None:
        return dt.replace(tzinfo=tz)
    else:
        return dt.astimezone(tz)

@app.route(f'/{str.lower(config.name)}.ics')
def index():
    today = date.today()
    start_of_week = today - timedelta(days=today.weekday())
    start_date = datetime.combine(start_of_week, time.min, tzinfo=tz)
    end_date = start_date + timedelta(days=30)

    output = Calendar()
    output.add('prodid', '-//Calendar Anonymiser//alin.ovh//')
    output.add('version', '2.0')

    # Parse with icalendar
    try:
        input = Calendar.from_ical(get_calendar())
        for component in input.walk():
            if component.name == "VEVENT":
                dtstart = fixup_date(component.get('dtstart').dt)
                dtend = fixup_date(component.get('dtend', component.get('dtstart')).dt)
                if dtstart >= start_date and dtend <= end_date:
                    ev = Event()
                    ev.add('summary', f'{config.name} Busy')
                    ev.DTSTART = dtstart
                    ev.DTEND = dtend
                    output.add_component(ev)
        return output.to_ical(), { "Content-Type": "text/plain" }
    except Exception as e:
        return f"Error parsing with icalendar: {str(e)}", 500

if __name__ == '__main__':
    app.run(debug=True)