add proxy for the first part and try to develop the second part

This commit is contained in:
Pierre Geeraert
2025-12-03 12:08:56 +00:00
parent 6c34d510e5
commit 460f18d85a
1095 changed files with 358799 additions and 5 deletions
@@ -0,0 +1,3 @@
from ics.alarm.audio import AudioAlarm
from ics.alarm.display import DisplayAlarm
from ics.alarm.email import EmailAlarm
@@ -0,0 +1,44 @@
from typing import Optional
from ics.alarm.base import BaseAlarm
from ics.grammar.parse import ContentLine
from typing import Union
from datetime import datetime, timedelta
from ics.serializers.alarm_serializer import AudioAlarmSerializer
from ics.parsers.alarm_parser import AudioAlarmParser
class AudioAlarm(BaseAlarm):
"""
A calendar event VALARM with AUDIO option.
"""
class Meta:
name = "VALARM"
parser = AudioAlarmParser
serializer = AudioAlarmSerializer
def __init__(
self,
trigger: Union[timedelta, datetime] = None,
repeat: int = None,
duration: timedelta = None,
):
super().__init__(trigger, repeat, duration)
self._sound: Optional[ContentLine] = None
@property
def action(self):
return "AUDIO"
@property
def sound(self):
return self._sound
@sound.setter
def sound(self, sound):
assert isinstance(sound, ContentLine)
self._sound = sound
@@ -0,0 +1,154 @@
import copy
from datetime import datetime, timedelta
from typing import Optional, Union, Any
from abc import ABCMeta, abstractmethod
from ics.component import Component
from ics.utils import (
get_arrow,
get_lines
)
from ics.grammar.parse import Container
from ics.serializers.alarm_serializer import BaseAlarmSerializer
from ics.parsers.alarm_parser import BaseAlarmParser
class BaseAlarm(Component, metaclass=ABCMeta):
"""
A calendar event VALARM base class
"""
class Meta:
name = "VALARM"
parser = BaseAlarmParser
serializer = BaseAlarmSerializer
def __init__(
self,
trigger: Union[timedelta, datetime] = None,
repeat: int = None,
duration: timedelta = None,
) -> None:
"""
Instantiates a new :class:`ics.alarm.BaseAlarm`.
Adheres to RFC5545 VALARM standard: http://icalendar.org/iCalendar-RFC-5545/3-6-6-alarm-component.html
Args:
trigger (datetime.timedelta OR datetime.datetime) : Timespan to alert before parent action, or absolute time to alert
repeat (integer) : How many times to repeat the alarm
duration (datetime.timedelta) : Duration between repeats
Raises:
ValueError: If trigger, repeat, or duration do not match the RFC spec.
"""
# Set initial values
self._trigger: Optional[Union[timedelta, datetime]] = None
self._repeat: Optional[int] = None
self._duration: Optional[timedelta] = None
# Validate and parse
self.trigger = trigger
# XOR repeat and duration
if (repeat is not None) and (duration is None):
raise ValueError(
"A definition of an alarm with a repeating trigger MUST include both the DURATION and REPEAT properties."
)
if repeat:
self.repeat = repeat
if duration:
self.duration = duration
self.extra = Container(name="VALARM")
@classmethod
def _from_container(cls, container: Container, *args: Any, **kwargs: Any):
ret = super()._from_container(container, *args, **kwargs)
get_lines(ret.extra, "ACTION", keep=False) # Just drop the ACTION line
return ret
@property
def trigger(self) -> Optional[Union[timedelta, datetime]]:
"""The trigger condition for the alarm
| Returns either a timedelta or datetime object
"""
return self._trigger
@trigger.setter
def trigger(self, value: Optional[Union[timedelta, datetime]]) -> None:
if isinstance(value, datetime):
value = get_arrow(value)
self._trigger = value
@property
def repeat(self) -> Optional[int]:
"""Number of times to repeat alarm
| Returns an integer for number of alarm repeats
| Value must be >= 0
"""
return self._repeat
@repeat.setter
def repeat(self, value: Optional[int]) -> None:
if value is not None and value < 0:
raise ValueError("Repeat must be great than or equal to 0.")
self._repeat = value
@property
def duration(self) -> Optional[timedelta]:
"""Duration between alarm repeats
| Returns a timedelta object
| Timespan must return positive total_seconds() value
"""
return self._duration
@duration.setter
def duration(self, value: Optional[timedelta]) -> None:
if value is not None and value.total_seconds() < 0:
raise ValueError("Alarm duration timespan must be positive.")
self._duration = value
@property
@abstractmethod
def action(self):
""" VALARM action to be implemented by concrete classes
"""
raise NotImplementedError("Base class cannot be instantiated directly")
def __repr__(self):
value = "{0} trigger:{1}".format(type(self).__name__, self.trigger)
if self.repeat:
value += " repeat:{0} duration:{1}".format(self.repeat, self.duration)
return "<{0}>".format(value)
def __ne__(self, other) -> bool:
return not self.__eq__(other)
def __eq__(self, other) -> bool:
"""Two alarms are considered equal if they have the same type and base values."""
return (
type(self) is type(other)
and self.trigger == other.trigger
and self.repeat == other.repeat
and self.duration == other.duration
)
def clone(self):
"""
Returns:
Alarm: an exact copy of self"""
clone = copy.copy(self)
clone.extra = clone.extra.clone()
return clone
@@ -0,0 +1,33 @@
from ics.alarm.base import BaseAlarm
import copy
from typing import Union
from datetime import datetime, timedelta
from ics.serializers.alarm_serializer import CustomAlarmSerializer
from ics.parsers.alarm_parser import CustomAlarmParser
class CustomAlarm(BaseAlarm):
"""
A calendar event VALARM with custom ACTION.
"""
class Meta:
name = "VALARM"
parser = CustomAlarmParser
serializer = CustomAlarmSerializer
def __init__(
self,
trigger: Union[timedelta, datetime] = None,
repeat: int = None,
duration: timedelta = None,
action: str = None,
) -> None:
super().__init__(trigger, repeat, duration)
self._action = action
@property
def action(self):
return self._action
@@ -0,0 +1,34 @@
from ics.alarm.base import BaseAlarm
from typing import Union
from datetime import datetime, timedelta
from ics.serializers.alarm_serializer import DisplayAlarmSerializer
from ics.parsers.alarm_parser import DisplayAlarmParser
class DisplayAlarm(BaseAlarm):
"""
A calendar event VALARM with DISPLAY option.
"""
class Meta:
name = "VALARM"
parser = DisplayAlarmParser
serializer = DisplayAlarmSerializer
def __init__(
self,
trigger: Union[timedelta, datetime] = None,
repeat: int = None,
duration: timedelta = None,
display_text: str = None,
):
super().__init__(trigger, repeat, duration)
self.display_text = display_text
@property
def action(self):
return "DISPLAY"
@@ -0,0 +1,38 @@
from ics.attendee import Attendee
from ics.alarm.base import BaseAlarm
from typing import Union, List
from datetime import datetime, timedelta
from ics.serializers.alarm_serializer import EmailAlarmSerializer
from ics.parsers.alarm_parser import EmailAlarmParser
class EmailAlarm(BaseAlarm):
"""
A calendar event VALARM with Email option.
"""
class Meta:
name = "VALARM"
parser = EmailAlarmParser
serializer = EmailAlarmSerializer
def __init__(
self,
trigger: Union[timedelta, datetime] = None,
repeat: int = None,
duration: timedelta = None,
subject: str = None,
body: str = None,
recipients: List[Attendee] = None,
):
super().__init__(trigger, repeat, duration)
self.subject = subject
self.body = body
self.recipients = recipients if recipients else []
@property
def action(self):
return "EMAIL"
@@ -0,0 +1,19 @@
from ics.alarm.base import BaseAlarm
from ics.serializers.alarm_serializer import NoneAlarmSerializer
from ics.parsers.alarm_parser import NoneAlarmParser
class NoneAlarm(BaseAlarm):
"""
A calendar event VALARM with NONE option.
"""
class Meta:
name = "VALARM"
parser = NoneAlarmParser
serializer = NoneAlarmSerializer
@property
def action(self):
return "NONE"
@@ -0,0 +1,28 @@
from ics.alarm.audio import AudioAlarm
from ics.alarm.none import NoneAlarm
from ics.alarm.custom import CustomAlarm
from ics.alarm.display import DisplayAlarm
from ics.alarm.email import EmailAlarm
from ics.utils import get_lines
def get_type_from_action(action_type):
if action_type == "DISPLAY":
return DisplayAlarm
elif action_type == "AUDIO":
return AudioAlarm
elif action_type == "NONE":
return NoneAlarm
elif action_type == 'EMAIL':
return EmailAlarm
else:
return CustomAlarm
def get_type_from_container(container):
action_type_lines = get_lines(container, "ACTION", keep=True)
if len(action_type_lines) > 1:
raise ValueError("Too many ACTION parameters in VALARM")
action_type = action_type_lines[0]
return get_type_from_action(action_type.value)