calendar public
This commit is contained in:
@@ -42,8 +42,10 @@
|
|||||||
./system-modules/nx2site.nix
|
./system-modules/nx2site.nix
|
||||||
./system-modules/postgres.nix
|
./system-modules/postgres.nix
|
||||||
./system-modules/nx2site/proxy.nix
|
./system-modules/nx2site/proxy.nix
|
||||||
|
./system-modules/calendar-publish.nix
|
||||||
./system-modules/nx2site/audiobookshelf.nix
|
./system-modules/nx2site/audiobookshelf.nix
|
||||||
./system-modules/nx2site/gitea.nix
|
./system-modules/nx2site/gitea.nix
|
||||||
|
./system-modules/nx2site/open-web-calendar.nix
|
||||||
./system-modules/nx2site/radicale.nix
|
./system-modules/nx2site/radicale.nix
|
||||||
# ./system-modules/nx2site/nextcloud.nix
|
# ./system-modules/nx2site/nextcloud.nix
|
||||||
./system-modules/nx2site/vaultwarden.nix
|
./system-modules/nx2site/vaultwarden.nix
|
||||||
|
|||||||
138
system-modules/calendar-publish.nix
Normal file
138
system-modules/calendar-publish.nix
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
{ config, pkgs, user, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = with pkgs; let
|
||||||
|
radicale-root = "/var/lib/radicale";
|
||||||
|
web-root = "/var/nginx/webroot";
|
||||||
|
in [
|
||||||
|
(writers.writePython3Bin "nx_cal_pub" {
|
||||||
|
libraries = with python3Packages; [
|
||||||
|
ical
|
||||||
|
ics
|
||||||
|
requests
|
||||||
|
dateutils
|
||||||
|
];
|
||||||
|
flakeIgnore = [ "E302" "E305" "E226" "E501" ];
|
||||||
|
} /*python */ ''
|
||||||
|
import pytz
|
||||||
|
import os
|
||||||
|
from ics import Calendar, Event
|
||||||
|
from ics.grammar.parse import ContentLine
|
||||||
|
from dateutil.rrule import rrulestr
|
||||||
|
from ics.event import datetime, timedelta
|
||||||
|
|
||||||
|
def combine_ics_from_directories(directories, output_file):
|
||||||
|
"""
|
||||||
|
Combine all .ics events from a list of directories into one .ics file, supporting recurring events.
|
||||||
|
|
||||||
|
:param directories: List of directories containing .ics files.
|
||||||
|
:param output_file: Path to the output .ics file.
|
||||||
|
"""
|
||||||
|
combined_calendar = Calendar()
|
||||||
|
|
||||||
|
for directory in directories:
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
print(f"Directory '{directory}' does not exist. Skipping.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
for filename in os.listdir(directory):
|
||||||
|
if filename.endswith(".ics"):
|
||||||
|
file_path = os.path.join(directory, filename)
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
calendar = Calendar(file.read())
|
||||||
|
for event in calendar.events:
|
||||||
|
# Handle recurring events
|
||||||
|
rrule_line = None
|
||||||
|
for line in event.extra:
|
||||||
|
if isinstance(line, ContentLine) and line.name == "RRULE":
|
||||||
|
rrule_line = line
|
||||||
|
break
|
||||||
|
|
||||||
|
if rrule_line:
|
||||||
|
# Convert UNTIL to UTC if DTSTART is timezone-aware
|
||||||
|
rrule_params = rrule_line.value.split(";")
|
||||||
|
rrule_dict = {}
|
||||||
|
for param in rrule_params:
|
||||||
|
key, value = param.split("=")
|
||||||
|
rrule_dict[key] = value
|
||||||
|
|
||||||
|
if "UNTIL" in rrule_dict and event.begin.tzinfo:
|
||||||
|
until = datetime.fromisoformat(rrule_dict["UNTIL"])
|
||||||
|
if until.tzinfo is None: # If UNTIL is naive, make it UTC
|
||||||
|
until = until.astimezone(pytz.UTC)
|
||||||
|
rrule_dict["UNTIL"] = until.astimezone(pytz.UTC).strftime("%Y%m%dT%H%M%SZ")
|
||||||
|
|
||||||
|
# Reconstruct RRULE string
|
||||||
|
rrule_fixed = ";".join(f"{key}={value}" for key, value in rrule_dict.items())
|
||||||
|
rrule = rrulestr(rrule_fixed, dtstart=event.begin.astimezone(pytz.timezone('CET')))
|
||||||
|
|
||||||
|
# Expand recurring events and filter based on the date
|
||||||
|
for occurrence in rrule:
|
||||||
|
notTooOld = occurrence.date() >= (datetime.now().astimezone(pytz.UTC) - timedelta(days=1)).date()
|
||||||
|
notTooFuturisic = occurrence.date() < (datetime.now().astimezone(pytz.UTC) + timedelta(days=60)).date()
|
||||||
|
if notTooOld and notTooFuturisic:
|
||||||
|
new_event = Event(
|
||||||
|
name="",
|
||||||
|
begin=occurrence,
|
||||||
|
end=occurrence + (event.end - event.begin),
|
||||||
|
transparent=event.transparent or True,
|
||||||
|
)
|
||||||
|
combined_calendar.events.add(new_event)
|
||||||
|
else:
|
||||||
|
# Regular events, directly add if within date range
|
||||||
|
if event.begin.astimezone(pytz.timezone('CET')).date() >= (datetime.now().astimezone(pytz.timezone('CET')) - timedelta(days=1)).date():
|
||||||
|
new_event = Event(
|
||||||
|
name="",
|
||||||
|
begin=event.begin,
|
||||||
|
end=event.end,
|
||||||
|
transparent=event.transparent or True,
|
||||||
|
)
|
||||||
|
combined_calendar.events.add(new_event)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading file '{file_path}': {e}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(output_file, 'w') as file:
|
||||||
|
file.writelines(combined_calendar.serialize_iter())
|
||||||
|
print(f"Combined .ics file saved to '{output_file}'")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving combined .ics file: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# List of directories containing .ics files
|
||||||
|
DIRECTORIES = [
|
||||||
|
"${radicale-root}/collections/collection-root/${user}/preservation",
|
||||||
|
"${radicale-root}/collections/collection-root/${user}/effort",
|
||||||
|
"${radicale-root}/collections/collection-root/${user}/experience",
|
||||||
|
"${radicale-root}/collections/collection-root/${user}/exposure",
|
||||||
|
"${radicale-root}/collections/collection-root/${user}/engagement",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Path to the output .ics file
|
||||||
|
OUTPUT_FILE = "${web-root}/schedule.ics"
|
||||||
|
|
||||||
|
combine_ics_from_directories(DIRECTORIES, OUTPUT_FILE)
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
systemd.timers."nx_cal_publish" = {
|
||||||
|
enable = true;
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnBootSec = "2m";
|
||||||
|
OnUnitActiveSec = "6h";
|
||||||
|
Unit = "nx_cal_publish.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."nx_cal_publish" = {
|
||||||
|
script = ''
|
||||||
|
nx_cal_publish
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "nx2";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
15
system-modules/nx2site/open-web-calendar.nix
Normal file
15
system-modules/nx2site/open-web-calendar.nix
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{ pkgs, domain, ... }:
|
||||||
|
{
|
||||||
|
services = {
|
||||||
|
open-web-calendar = {
|
||||||
|
enable = true;
|
||||||
|
domain = "cal.${domain}";
|
||||||
|
package = pkgs.open-web-calendar;
|
||||||
|
settings = {
|
||||||
|
# PORT = 21342;
|
||||||
|
};
|
||||||
|
calendarSettings = {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user