-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from RedRem95/custom_ical
Custom ical implementation
- Loading branch information
Showing
8 changed files
with
188 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "0.0.4" | ||
__version__ = "0.0.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from clickup_to_ical.ical.calendar import Calendar | ||
from clickup_to_ical.ical.event import Event, User | ||
|
||
__all__ = ['Calendar', 'Event', 'User'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from dataclasses import dataclass, field | ||
from datetime import timedelta | ||
from typing import List | ||
|
||
import pytz | ||
|
||
from clickup_to_ical.ical.event import Event | ||
|
||
|
||
def _time_to_duration(td: timedelta) -> str: | ||
days = td.days | ||
hours, remainder = divmod(td.seconds, 3600) | ||
minutes, seconds = divmod(remainder, 60) | ||
return f"P{days if days > 0 else ''}T{f'{hours}H' if hours > 0 else ''}{f'{minutes}M' if minutes > 0 else ''}{f'{seconds}S' if seconds > 0 else ''}" | ||
|
||
|
||
@dataclass(frozen=True) | ||
class Calendar: | ||
version: str | ||
prodid: str | ||
method: str = None | ||
calscale: str = None | ||
calendar_name: str = None | ||
calendar_description: str = None | ||
calendar_timezone = None | ||
calendar_ttl: timedelta = None | ||
events: List[Event] = field(default_factory=list) | ||
|
||
def __str__(self): | ||
ret = [ | ||
f"VERSION:{self.version}", | ||
f"PRODID:{self.prodid}", | ||
] | ||
if self.method is not None: | ||
ret.append(f"METHOD:{self.method}") | ||
if self.calscale is not None: | ||
ret.append(f"CALSCALE:{self.calscale}") | ||
ret.append(f"X-MICROSOFT-CALSCALE:{self.calscale}") | ||
if self.calendar_name is not None: | ||
ret.append(f"X-WR-CALNAME:{self.calendar_name}") | ||
if self.calendar_description is not None: | ||
ret.append(f"X-WR-CALDESC:{self.calendar_description}") | ||
if self.calendar_timezone is not None and self.calendar_timezone != pytz.UTC: | ||
ret.append(f"X-WR-TIMEZONE:{str(self.calendar_timezone)}") | ||
if self.calendar_ttl is not None: | ||
ret.append(f"X-PUBLISHED-TTL:{_time_to_duration(self.calendar_ttl)}") | ||
|
||
ret.extend(str(event) for event in self.events) | ||
|
||
return "BEGIN:VCALENDAR\n{}\nEND:VCALENDAR".format('\n'.join(ret)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
from dataclasses import dataclass, field | ||
from datetime import datetime, timezone | ||
from typing import List | ||
|
||
import pytz | ||
|
||
|
||
def _datetime_to_str(dt_name: str, dt: datetime) -> str: | ||
base = f"{dt.year:04d}{dt.month:02d}{dt.day:02d}T{dt.hour:02d}{dt.minute:02d}{dt.second:02d}" | ||
if dt.tzinfo is None: | ||
return f"{dt_name}:{base}" | ||
elif dt.tzinfo in (pytz.UTC, timezone.utc, pytz.timezone("UTC")): | ||
return f"{dt_name}:{base}Z" | ||
else: | ||
return f"{dt_name};TZID={dt.tzinfo};VALUE=DATE:{base}" | ||
|
||
|
||
@dataclass(frozen=True) | ||
class User: | ||
uri: str | ||
name: str = None | ||
|
||
def __str__(self, user_type: str = "ATTENDEE"): | ||
return f"{user_type}{'' if self.name is None else f';CN={self.name}'}:{self.uri}" | ||
|
||
|
||
@dataclass(frozen=True) | ||
class Event: | ||
uid: str | ||
dtstamp: datetime | ||
dtstart: datetime | ||
dtend: datetime = None | ||
created: datetime = None | ||
last_modified: datetime = None | ||
url: str = None | ||
location: str = None | ||
summary: str = None | ||
description: str = None | ||
sequence: int = None | ||
transp: str = None | ||
status: str = None | ||
priority: int = None | ||
organizer: User = None | ||
attendees: List[User] = field(default_factory=list) | ||
|
||
def __str__(self): | ||
ret = [ | ||
f"UID:{self.uid}", | ||
_datetime_to_str("DTSTAMP", self.dtstamp), | ||
_datetime_to_str("DTSTART", self.dtstart) | ||
] | ||
|
||
if self.dtend is not None: | ||
ret.append(_datetime_to_str("DTEND", self.dtend)) | ||
if self.created is not None: | ||
ret.append(_datetime_to_str("CREATED", self.created)) | ||
if self.last_modified is not None: | ||
ret.append(_datetime_to_str("LAST-MODIFIED", self.created)) | ||
if self.url is not None: | ||
ret.append(f"URL:{self.url}") | ||
if self.location is not None: | ||
ret.append(f"LOCATION:{self.location}") | ||
if self.summary is not None: | ||
ret.append(f"SUMMARY:{self.summary}") | ||
if self.description is not None: | ||
ret.append(f"DESCRIPTION:{self.description}") | ||
if self.sequence is not None: | ||
ret.append(f"SEQUENCE:{self.sequence}") | ||
if self.transp is not None: | ||
ret.append(f"TRANSP:{self.transp}") | ||
if self.status is not None: | ||
ret.append(f"STATUS:{self.status}") | ||
if self.priority is not None: | ||
ret.append(f"PRIORITY:{self.priority}") | ||
if self.organizer is not None: | ||
ret.append(self.organizer.__str__(user_type="ORGANIZER")) | ||
ret.extend(x.__str__(user_type="ATTENDEE") for x in self.attendees) | ||
|
||
return "BEGIN:VEVENT\n{}\nEND:VEVENT".format("\n".join(ret)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters