Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/cal/alarm.py: 51%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""":rfc:`5545` VALARM component."""
3from __future__ import annotations
5from datetime import date, datetime, timedelta
6from typing import TYPE_CHECKING, NamedTuple
8from icalendar.attr import (
9 CONCEPTS_TYPE_SETTER,
10 LINKS_TYPE_SETTER,
11 RELATED_TO_TYPE_SETTER,
12 attendees_property,
13 create_single_property,
14 description_property,
15 property_del_duration,
16 property_get_duration,
17 property_set_duration,
18 single_int_property,
19 single_string_property,
20 single_utc_property,
21 summary_property,
22 uid_property,
23)
24from icalendar.cal.component import Component
25from icalendar.cal.examples import get_example
27if TYPE_CHECKING:
28 import uuid
30 from icalendar.prop import vCalAddress
33class Alarm(Component):
34 """
35 A "VALARM" calendar component is a grouping of component
36 properties that defines an alarm or reminder for an event or a
37 to-do. For example, it may be used to define a reminder for a
38 pending event or an overdue to-do.
40 Example:
42 The following example creates an alarm which uses an audio file
43 from an FTP server.
45 .. code-block:: pycon
47 >>> from icalendar import Alarm
48 >>> alarm = Alarm.example()
49 >>> print(alarm.to_ical().decode())
50 BEGIN:VALARM
51 ACTION:AUDIO
52 ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/sounds/bell-01.aud
53 DURATION:PT15M
54 REPEAT:4
55 TRIGGER;VALUE=DATE-TIME:19970317T133000Z
56 END:VALARM
57 """
59 name = "VALARM"
60 # some properties MAY/MUST/MUST NOT appear depending on ACTION value
61 required = (
62 "ACTION",
63 "TRIGGER",
64 )
65 singletons = (
66 "ATTACH",
67 "ACTION",
68 "DESCRIPTION",
69 "SUMMARY",
70 "TRIGGER",
71 "DURATION",
72 "REPEAT",
73 "UID",
74 "PROXIMITY",
75 "ACKNOWLEDGED",
76 )
77 inclusive = (
78 (
79 "DURATION",
80 "REPEAT",
81 ),
82 (
83 "SUMMARY",
84 "ATTENDEE",
85 ),
86 )
87 multiple = ("ATTENDEE", "ATTACH", "RELATED-TO")
89 REPEAT = single_int_property(
90 "REPEAT",
91 0,
92 """The number of additional times the alarm is triggered after the initial trigger.
94 Defaults to ``0``, meaning the alarm fires once. To repeat the alarm,
95 set both :attr:`REPEAT` and :attr:`DURATION`. The :attr:`DURATION`
96 sets the gap between repetitions. ``REPEAT`` is the count of *additional*
97 triggers, so a ``REPEAT`` of ``2`` produces three alarms in total
98 (the initial trigger plus two repeats).
100 Conforming with :rfc:`5545#section-3.8.6.2`, this property can appear
101 once in an :class:`~icalendar.cal.alarm.Alarm` component and must be
102 paired with :attr:`DURATION`.
104 Example:
105 Build an alarm that fires once and then repeats twice at
106 five-minute intervals.
108 .. code-block:: pycon
110 >>> from datetime import timedelta
111 >>> from icalendar import Alarm
112 >>> alarm = Alarm()
113 >>> alarm.TRIGGER = timedelta(minutes=-15)
114 >>> alarm.DURATION = timedelta(minutes=5)
115 >>> alarm.REPEAT = 2
116 >>> alarm.REPEAT
117 2
118 """,
119 )
121 DURATION = property(
122 property_get_duration,
123 property_set_duration,
124 property_del_duration,
125 """The delay between repeated triggers of a repeating alarm.
127 Returns a :class:`datetime.timedelta` or ``None`` when the alarm
128 has no :attr:`DURATION` set. Setting this attribute accepts a
129 :class:`~datetime.timedelta`; deleting it removes the property
130 from the component.
132 :attr:`DURATION` is meaningful only for repeating alarms and must
133 be paired with :attr:`REPEAT`. The two together produce
134 ``REPEAT`` additional triggers, each spaced by ``DURATION`` after
135 the initial trigger.
137 Conforming with :rfc:`5545#section-3.8.2.5`, the ``DURATION`` property
138 can appear once in an :class:`~icalendar.cal.alarm.Alarm` component.
140 Example:
141 Pair :attr:`DURATION` with :attr:`REPEAT` to produce three
142 triggers spaced ten minutes apart.
144 .. code-block:: pycon
146 >>> from datetime import timedelta
147 >>> from icalendar import Alarm
148 >>> alarm = Alarm()
149 >>> alarm.TRIGGER = timedelta(minutes=-30)
150 >>> alarm.DURATION = timedelta(minutes=10)
151 >>> alarm.REPEAT = 2
152 >>> alarm.DURATION
153 datetime.timedelta(seconds=600)
154 """,
155 )
157 ACKNOWLEDGED = single_utc_property(
158 "ACKNOWLEDGED",
159 """This is defined in RFC 9074:
161 Purpose: This property specifies the UTC date and time at which the
162 corresponding alarm was last sent or acknowledged.
164 This property is used to specify when an alarm was last sent or acknowledged.
165 This allows clients to determine when a pending alarm has been acknowledged
166 by a calendar user so that any alerts can be dismissed across multiple devices.
167 It also allows clients to track repeating alarms or alarms on recurring events or
168 to-dos to ensure that the right number of missed alarms can be tracked.
170 Clients SHOULD set this property to the current date-time value in UTC
171 when a calendar user acknowledges a pending alarm. Certain kinds of alarms,
172 such as email-based alerts, might not provide feedback as to when the calendar user
173 sees them. For those kinds of alarms, the client SHOULD set this property
174 when the alarm is triggered and the action is successfully carried out.
176 When an alarm is triggered on a client, clients can check to see if an "ACKNOWLEDGED"
177 property is present. If it is, and the value of that property is greater than or
178 equal to the computed trigger time for the alarm, then the client SHOULD NOT trigger
179 the alarm. Similarly, if an alarm has been triggered and
180 an "alert" has been presented to a calendar user, clients can monitor
181 the iCalendar data to determine whether an "ACKNOWLEDGED" property is added or
182 changed in the alarm component. If the value of any "ACKNOWLEDGED" property
183 in the alarm changes and is greater than or equal to the trigger time of the alarm,
184 then clients SHOULD dismiss or cancel any "alert" presented to the calendar user.
185 """,
186 )
188 TRIGGER = create_single_property(
189 "TRIGGER",
190 "dt",
191 (datetime, timedelta),
192 timedelta | datetime | None,
193 """Purpose: This property specifies when an alarm will trigger.
195 Value Type: The default value type is DURATION. The value type can
196 be set to a DATE-TIME value type, in which case the value MUST
197 specify a UTC-formatted DATE-TIME value.
199 Either a positive or negative duration may be specified for the
200 "TRIGGER" property. An alarm with a positive duration is
201 triggered after the associated start or end of the event or to-do.
202 An alarm with a negative duration is triggered before the
203 associated start or end of the event or to-do.""",
204 )
206 @property
207 def TRIGGER_RELATED(self) -> str:
208 """The RELATED parameter of the TRIGGER property.
210 Values are either "START" (default) or "END".
212 A value of START will set the alarm to trigger off the
213 start of the associated event or to-do. A value of END will set
214 the alarm to trigger off the end of the associated event or to-do.
216 In this example, we create an alarm that triggers two hours after the
217 end of its parent component:
219 >>> from icalendar import Alarm
220 >>> from datetime import timedelta
221 >>> alarm = Alarm()
222 >>> alarm.TRIGGER = timedelta(hours=2)
223 >>> alarm.TRIGGER_RELATED = "END"
224 """
225 trigger = self.get("TRIGGER")
226 if trigger is None:
227 return "START"
228 return trigger.params.get("RELATED", "START")
230 @TRIGGER_RELATED.setter
231 def TRIGGER_RELATED(self, value: str):
232 """Set "START" or "END"."""
233 trigger = self.get("TRIGGER")
234 if trigger is None:
235 raise ValueError(
236 "You must set a TRIGGER before setting the RELATED parameter."
237 )
238 trigger.params["RELATED"] = value
240 class Triggers(NamedTuple):
241 """The computed times of alarm triggers.
243 start - triggers relative to the start of the Event or Todo (timedelta)
245 end - triggers relative to the end of the Event or Todo (timedelta)
247 absolute - triggers at a datetime in UTC
248 """
250 start: tuple[timedelta]
251 end: tuple[timedelta]
252 absolute: tuple[datetime]
254 @property
255 def triggers(self):
256 """The computed triggers of an Alarm.
258 This takes the TRIGGER, DURATION and REPEAT properties into account.
260 Here, we create an alarm that triggers 3 times before the start of the
261 parent component:
263 >>> from icalendar import Alarm
264 >>> from datetime import timedelta
265 >>> alarm = Alarm()
266 >>> alarm.TRIGGER = timedelta(hours=-4) # trigger 4 hours before START
267 >>> alarm.DURATION = timedelta(hours=1) # after 1 hour trigger again
268 >>> alarm.REPEAT = 2 # trigger 2 more times
269 >>> alarm.triggers.start == (timedelta(hours=-4), timedelta(hours=-3), timedelta(hours=-2))
270 True
271 >>> alarm.triggers.end
272 ()
273 >>> alarm.triggers.absolute
274 ()
275 """
276 start = []
277 end = []
278 absolute = []
279 trigger = self.TRIGGER
280 if trigger is not None:
281 if isinstance(trigger, date):
282 absolute.append(trigger)
283 add = absolute
284 elif self.TRIGGER_RELATED == "START":
285 start.append(trigger)
286 add = start
287 else:
288 end.append(trigger)
289 add = end
290 duration = self.DURATION
291 if duration is not None:
292 for _ in range(self.REPEAT):
293 add.append(add[-1] + duration)
294 return self.Triggers(
295 start=tuple(start), end=tuple(end), absolute=tuple(absolute)
296 )
298 uid = single_string_property(
299 "UID",
300 uid_property.__doc__,
301 "X-ALARMUID",
302 )
303 summary = summary_property
304 description = description_property
305 attendees = attendees_property
307 @classmethod
308 def new(
309 cls,
310 /,
311 attendees: list[vCalAddress] | None = None,
312 concepts: CONCEPTS_TYPE_SETTER = None,
313 description: str | None = None,
314 links: LINKS_TYPE_SETTER = None,
315 refids: list[str] | str | None = None,
316 related_to: RELATED_TO_TYPE_SETTER = None,
317 summary: str | None = None,
318 uid: str | uuid.UUID | None = None,
319 ):
320 """Create a new alarm with all required properties.
322 This creates a new Alarm in accordance with :rfc:`5545`.
324 Parameters:
325 attendees: The :attr:`attendees` of the alarm.
326 concepts: The :attr:`~icalendar.Component.concepts` of the alarm.
327 description: The :attr:`description` of the alarm.
328 links: The :attr:`~icalendar.Component.links` of the alarm.
329 refids: :attr:`~icalendar.Component.refids` of the alarm.
330 related_to: :attr:`~icalendar.Component.related_to` of the alarm.
331 summary: The :attr:`summary` of the alarm.
332 uid: The :attr:`uid` of the alarm.
334 Returns:
335 :class:`Alarm`
337 Raises:
338 ~error.InvalidCalendar: If the content is not valid
339 according to :rfc:`5545`.
341 .. warning:: As time progresses, we will be stricter with the validation.
342 """
343 alarm: Alarm = super().new(
344 links=links,
345 related_to=related_to,
346 refids=refids,
347 concepts=concepts,
348 )
349 alarm.summary = summary
350 alarm.description = description
351 alarm.uid = uid
352 alarm.attendees = attendees
353 return alarm
355 @classmethod
356 def example(cls, name: str = "example") -> Alarm:
357 """Return the alarm example with the given name."""
358 return cls.from_ical(get_example("alarms", name))
361__all__ = ["Alarm"]