from __future__ import annotations from datetime import UTC, datetime, time, timedelta from ..models import TaskInstance, User def _ics_escape(value: str | None) -> str: text = (value or "").replace("\\", "\\\\") text = text.replace(";", "\\;").replace(",", "\\,") text = text.replace("\r\n", "\\n").replace("\n", "\\n").replace("\r", "\\n") return text def _format_date(value) -> str: return value.strftime("%Y%m%d") def _format_timestamp(value: datetime | None) -> str: timestamp = value or datetime.now(UTC).replace(tzinfo=None) return timestamp.strftime("%Y%m%dT%H%M%SZ") def build_calendar_feed(user: User, base_url: str) -> str: tasks = ( TaskInstance.query.filter_by(assigned_user_id=user.id) .order_by(TaskInstance.due_date.asc(), TaskInstance.id.asc()) .all() ) lines = [ "BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//hnz.io//Putzliga//DE", "CALSCALE:GREGORIAN", "METHOD:PUBLISH", f"X-WR-CALNAME:{_ics_escape(f'Putzliga - {user.name}')}", f"X-WR-CALDESC:{_ics_escape('Persoenlicher Aufgabenfeed aus Putzliga')}", ] for task in tasks: due_end = task.due_date + timedelta(days=1) description = _ics_escape(task.description) lines.extend( [ "BEGIN:VEVENT", f"UID:taskinstance-{task.id}@putzliga", f"DTSTAMP:{_format_timestamp(task.updated_at)}", f"LAST-MODIFIED:{_format_timestamp(task.updated_at)}", f"SUMMARY:{_ics_escape(task.title)}", f"DESCRIPTION:{description}", f"DTSTART;VALUE=DATE:{_format_date(task.due_date)}", f"DTEND;VALUE=DATE:{_format_date(due_end)}", f"URL:{_ics_escape(base_url)}", "END:VEVENT", ] ) lines.append("END:VCALENDAR") return "\r\n".join(lines) + "\r\n"