Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/cal/event.py: 61%
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` VEVENT component."""
3from __future__ import annotations
5import uuid
6from datetime import date, datetime, timedelta
7from typing import TYPE_CHECKING, Literal, Sequence
9from icalendar.attr import (
10 CONCEPTS_TYPE_SETTER,
11 LINKS_TYPE_SETTER,
12 RELATED_TO_TYPE_SETTER,
13 X_MOZ_LASTACK_property,
14 X_MOZ_SNOOZE_TIME_property,
15 attendees_property,
16 categories_property,
17 class_property,
18 color_property,
19 conferences_property,
20 contacts_property,
21 create_single_property,
22 description_property,
23 exdates_property,
24 get_duration_property,
25 get_end_property,
26 get_start_end_duration_with_validation,
27 get_start_property,
28 images_property,
29 location_property,
30 organizer_property,
31 priority_property,
32 property_del_duration,
33 property_doc_duration_template,
34 property_get_duration,
35 property_set_duration,
36 rdates_property,
37 rrules_property,
38 sequence_property,
39 set_duration_with_locking,
40 set_end_with_locking,
41 set_start_with_locking,
42 status_property,
43 summary_property,
44 transparency_property,
45 uid_property,
46 url_property,
47)
48from icalendar.cal.component import Component
49from icalendar.cal.examples import get_example
51if TYPE_CHECKING:
52 from icalendar.alarms import Alarms
53 from icalendar.enums import CLASS, STATUS, TRANSP
54 from icalendar.prop import vCalAddress
55 from icalendar.prop.conference import Conference
58class Event(Component):
59 """A grouping of component properties that describe an event.
61 Description:
62 A "VEVENT" calendar component is a grouping of
63 component properties, possibly including "VALARM" calendar
64 components, that represents a scheduled amount of time on a
65 calendar. For example, it can be an activity; such as a one-hour
66 long, department meeting from 8:00 AM to 9:00 AM, tomorrow.
67 Generally, an event will take up time on an individual calendar.
68 Hence, the event will appear as an opaque interval in a search for
69 busy time. Alternately, the event can have its Time Transparency
70 set to "TRANSPARENT" in order to prevent blocking of the event in
71 searches for busy time.
73 The "VEVENT" is also the calendar component used to specify an
74 anniversary or daily reminder within a calendar. These events
75 have a DATE value type for the "DTSTART" property instead of the
76 default value type of DATE-TIME. If such a "VEVENT" has a "DTEND"
77 property, it MUST be specified as a DATE value also. The
78 anniversary type of "VEVENT" can span more than one date (i.e.,
79 "DTEND" property value is set to a calendar date after the
80 "DTSTART" property value). If such a "VEVENT" has a "DURATION"
81 property, it MUST be specified as a "dur-day" or "dur-week" value.
83 The "DTSTART" property for a "VEVENT" specifies the inclusive
84 start of the event. For recurring events, it also specifies the
85 very first instance in the recurrence set. The "DTEND" property
86 for a "VEVENT" calendar component specifies the non-inclusive end
87 of the event. For cases where a "VEVENT" calendar component
88 specifies a "DTSTART" property with a DATE value type but no
89 "DTEND" nor "DURATION" property, the event's duration is taken to
90 be one day. For cases where a "VEVENT" calendar component
91 specifies a "DTSTART" property with a DATE-TIME value type but no
92 "DTEND" property, the event ends on the same calendar date and
93 time of day specified by the "DTSTART" property.
95 The "VEVENT" calendar component cannot be nested within another
96 calendar component. However, "VEVENT" calendar components can be
97 related to each other or to a "VTODO" or to a "VJOURNAL" calendar
98 component with the "RELATED-TO" property.
100 Examples:
101 The following is an example of the "VEVENT" calendar
102 component used to represent a meeting that will also be opaque to
103 searches for busy time:
105 .. code-block:: text
107 BEGIN:VEVENT
108 UID:19970901T130000Z-123401@example.com
109 DTSTAMP:19970901T130000Z
110 DTSTART:19970903T163000Z
111 DTEND:19970903T190000Z
112 SUMMARY:Annual Employee Review
113 CLASS:PRIVATE
114 CATEGORIES:BUSINESS,HUMAN RESOURCES
115 END:VEVENT
117 The following is an example of the "VEVENT" calendar component
118 used to represent a reminder that will not be opaque, but rather
119 transparent, to searches for busy time:
121 .. code-block:: text
123 BEGIN:VEVENT
124 UID:19970901T130000Z-123402@example.com
125 DTSTAMP:19970901T130000Z
126 DTSTART:19970401T163000Z
127 DTEND:19970402T010000Z
128 SUMMARY:Laurel is in sensitivity awareness class.
129 CLASS:PUBLIC
130 CATEGORIES:BUSINESS,HUMAN RESOURCES
131 TRANSP:TRANSPARENT
132 END:VEVENT
134 The following is an example of the "VEVENT" calendar component
135 used to represent an anniversary that will occur annually:
137 .. code-block:: text
139 BEGIN:VEVENT
140 UID:19970901T130000Z-123403@example.com
141 DTSTAMP:19970901T130000Z
142 DTSTART;VALUE=DATE:19971102
143 SUMMARY:Our Blissful Anniversary
144 TRANSP:TRANSPARENT
145 CLASS:CONFIDENTIAL
146 CATEGORIES:ANNIVERSARY,PERSONAL,SPECIAL OCCASION
147 RRULE:FREQ=YEARLY
148 END:VEVENT
150 The following is an example of the "VEVENT" calendar component
151 used to represent a multi-day event scheduled from June 28th, 2007
152 to July 8th, 2007 inclusively. Note that the "DTEND" property is
153 set to July 9th, 2007, since the "DTEND" property specifies the
154 non-inclusive end of the event.
156 .. code-block:: text
158 BEGIN:VEVENT
159 UID:20070423T123432Z-541111@example.com
160 DTSTAMP:20070423T123432Z
161 DTSTART;VALUE=DATE:20070628
162 DTEND;VALUE=DATE:20070709
163 SUMMARY:Festival International de Jazz de Montreal
164 TRANSP:TRANSPARENT
165 END:VEVENT
167 Create a new Event:
169 .. code-block:: python
171 >>> from icalendar import Event
172 >>> from datetime import datetime
173 >>> event = Event.new(start=datetime(2021, 1, 1, 12, 30, 0))
174 >>> print(event.to_ical())
175 BEGIN:VEVENT
176 DTSTART:20210101T123000
177 DTSTAMP:20250517T080612Z
178 UID:d755cef5-2311-46ed-a0e1-6733c9e15c63
179 END:VEVENT
181 """
183 name = "VEVENT"
185 canonical_order = (
186 "SUMMARY",
187 "DTSTART",
188 "DTEND",
189 "DURATION",
190 "DTSTAMP",
191 "UID",
192 "RECURRENCE-ID",
193 "SEQUENCE",
194 "RRULE",
195 "RDATE",
196 "EXDATE",
197 )
199 required = (
200 "UID",
201 "DTSTAMP",
202 )
203 singletons = (
204 "CLASS",
205 "CREATED",
206 "COLOR",
207 "DESCRIPTION",
208 "DTSTART",
209 "GEO",
210 "LAST-MODIFIED",
211 "LOCATION",
212 "ORGANIZER",
213 "PRIORITY",
214 "DTSTAMP",
215 "SEQUENCE",
216 "STATUS",
217 "SUMMARY",
218 "TRANSP",
219 "URL",
220 "RECURRENCE-ID",
221 "DTEND",
222 "DURATION",
223 "UID",
224 )
225 exclusive = (
226 "DTEND",
227 "DURATION",
228 )
229 multiple = (
230 "ATTACH",
231 "ATTENDEE",
232 "CATEGORIES",
233 "COMMENT",
234 "CONTACT",
235 "EXDATE",
236 "RSTATUS",
237 "RELATED",
238 "RESOURCES",
239 "RDATE",
240 "RRULE",
241 )
242 ignore_exceptions = True
244 @property
245 def alarms(self) -> Alarms:
246 """Compute the alarm times for this component.
248 >>> from icalendar import Event
249 >>> event = Event.example("rfc_9074_example_1")
250 >>> len(event.alarms.times)
251 1
252 >>> alarm_time = event.alarms.times[0]
253 >>> alarm_time.trigger # The time when the alarm pops up
254 datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='America/New_York'))
255 >>> alarm_time.is_active() # This alarm has not been acknowledged
256 True
258 Note that this only uses DTSTART and DTEND, but ignores
259 RDATE, EXDATE, and RRULE properties.
260 """
261 from icalendar.alarms import Alarms
263 return Alarms(self)
265 @classmethod
266 def example(cls, name: str = "rfc_9074_example_3") -> Event:
267 """Return the calendar example with the given name."""
268 return cls.from_ical(get_example("events", name))
270 DTSTART = create_single_property(
271 "DTSTART",
272 "dt",
273 (datetime, date),
274 date,
275 'The "DTSTART" property for a "VEVENT" specifies the inclusive start of the event.', # noqa: E501
276 )
277 DTEND = create_single_property(
278 "DTEND",
279 "dt",
280 (datetime, date),
281 date,
282 'The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event.', # noqa: E501
283 )
285 def _get_start_end_duration(self):
286 """Verify the calendar validity and return the right attributes."""
287 return get_start_end_duration_with_validation(
288 self, "DTSTART", "DTEND", "VEVENT"
289 )
291 DURATION = property(
292 property_get_duration,
293 property_set_duration,
294 property_del_duration,
295 property_doc_duration_template.format(component="VEVENT"),
296 )
298 @property
299 def duration(self) -> timedelta:
300 """The duration of the VEVENT.
302 Returns the DURATION property if set, otherwise calculated from start and end.
303 When setting duration, the end time is automatically calculated from start +
304 duration.
305 You can set the duration to automatically adjust the end time while keeping
306 start locked.
308 Setting the duration will:
309 1. Keep the start time locked (unchanged)
310 2. Adjust the end time to start + duration
311 3. Remove any existing DTEND property
312 4. Set the DURATION property
313 """
314 return get_duration_property(self)
316 @duration.setter
317 def duration(self, value: timedelta):
318 if not isinstance(value, timedelta):
319 raise TypeError(f"Use timedelta, not {type(value).__name__}.")
321 # Use the set_duration method with default start-locked behavior
322 self.set_duration(value, locked="start")
324 @property
325 def start(self) -> date | datetime:
326 """The start of the event.
328 Invalid values raise an InvalidCalendar.
329 If there is no start, we also raise an IncompleteComponent error.
331 You can get the start, end and duration of an event as follows:
333 >>> from datetime import datetime
334 >>> from icalendar import Event
335 >>> event = Event()
336 >>> event.start = datetime(2021, 1, 1, 12)
337 >>> event.end = datetime(2021, 1, 1, 12, 30) # 30 minutes
338 >>> event.duration # 1800 seconds == 30 minutes
339 datetime.timedelta(seconds=1800)
340 >>> print(event.to_ical())
341 BEGIN:VEVENT
342 DTSTART:20210101T120000
343 DTEND:20210101T123000
344 END:VEVENT
345 """
346 return get_start_property(self)
348 @start.setter
349 def start(self, start: date | datetime | None):
350 """Set the start."""
351 self.DTSTART = start
353 @property
354 def end(self) -> date | datetime:
355 """The end of the event.
357 Invalid values raise an InvalidCalendar error.
358 If there is no end, we also raise an IncompleteComponent error.
359 """
360 return get_end_property(self, "DTEND")
362 @end.setter
363 def end(self, end: date | datetime | None):
364 """Set the end."""
365 self.DTEND = end
367 def set_duration(
368 self, duration: timedelta | None, locked: Literal["start", "end"] = "start"
369 ):
370 """Set the duration of the event relative to either start or end.
372 Args:
373 duration: The duration to set, or None to convert to DURATION property
374 locked: Which property to keep unchanged ('start' or 'end')
375 """
376 set_duration_with_locking(self, duration, locked, "DTEND")
378 def set_start(
379 self, start: date | datetime, locked: Literal["duration", "end"] | None = None
380 ):
381 """Set the start and keep the duration or end of the event.
383 Args:
384 start: The start time to set
385 locked: Which property to keep unchanged ('duration', 'end', or None
386 for auto-detect)
387 """
388 set_start_with_locking(self, start, locked, "DTEND")
390 def set_end(
391 self, end: date | datetime, locked: Literal["start", "duration"] = "start"
392 ):
393 """Set the end of the component, keeping either the start or the duration same.
395 Args:
396 end: The end time to set
397 locked: Which property to keep unchanged ('start' or 'duration')
398 """
399 set_end_with_locking(self, end, locked, "DTEND")
401 X_MOZ_SNOOZE_TIME = X_MOZ_SNOOZE_TIME_property
402 X_MOZ_LASTACK = X_MOZ_LASTACK_property
403 color = color_property
404 sequence = sequence_property
405 categories = categories_property
406 rdates = rdates_property
407 exdates = exdates_property
408 rrules = rrules_property
409 uid = uid_property
410 summary = summary_property
411 description = description_property
412 classification = class_property
413 url = url_property
414 organizer = organizer_property
415 location = location_property
416 priority = priority_property
417 contacts = contacts_property
418 transparency = transparency_property
419 status = status_property
420 attendees = attendees_property
421 images = images_property
422 conferences = conferences_property
424 @classmethod
425 def new(
426 cls,
427 /,
428 attendees: list[vCalAddress] | None = None,
429 categories: Sequence[str] = (),
430 classification: CLASS | None = None,
431 color: str | None = None,
432 comments: list[str] | str | None = None,
433 concepts: CONCEPTS_TYPE_SETTER = None,
434 conferences: list[Conference] | None = None,
435 contacts: list[str] | str | None = None,
436 created: date | None = None,
437 description: str | None = None,
438 end: date | datetime | None = None,
439 last_modified: date | None = None,
440 links: LINKS_TYPE_SETTER = None,
441 location: str | None = None,
442 organizer: vCalAddress | str | None = None,
443 priority: int | None = None,
444 refids: list[str] | str | None = None,
445 related_to: RELATED_TO_TYPE_SETTER = None,
446 sequence: int | None = None,
447 stamp: date | None = None,
448 start: date | datetime | None = None,
449 status: STATUS | None = None,
450 transparency: TRANSP | None = None,
451 summary: str | None = None,
452 uid: str | uuid.UUID | None = None,
453 url: str | None = None,
454 ):
455 """Create a new event with all required properties.
457 This creates a new Event in accordance with :rfc:`5545`.
459 Arguments:
460 attendees: The :attr:`attendees` of the event.
461 categories: The :attr:`categories` of the event.
462 classification: The :attr:`classification` of the event.
463 color: The :attr:`color` of the event.
464 comments: The :attr:`~icalendar.Component.comments` of the event.
465 concepts: The :attr:`~icalendar.Component.concepts` of the event.
466 conferences: The :attr:`conferences` of the event.
467 created: The :attr:`~icalendar.Component.created` of the event.
468 description: The :attr:`description` of the event.
469 end: The :attr:`end` of the event.
470 last_modified: The :attr:`~icalendar.Component.last_modified` of the event.
471 links: The :attr:`~icalendar.Component.links` of the event.
472 location: The :attr:`location` of the event.
473 organizer: The :attr:`organizer` of the event.
474 priority: The :attr:`priority` of the event.
475 refids: :attr:`~icalendar.Component.refids` of the event.
476 related_to: :attr:`~icalendar.Component.related_to` of the event.
477 sequence: The :attr:`sequence` of the event.
478 stamp: The :attr:`~icalendar.Component.stamp` of the event.
479 If None, this is set to the current time.
480 start: The :attr:`start` of the event.
481 status: The :attr:`status` of the event.
482 summary: The :attr:`summary` of the event.
483 transparency: The :attr:`transparency` of the event.
484 uid: The :attr:`uid` of the event.
485 If None, this is set to a new :func:`uuid.uuid4`.
486 url: The :attr:`url` of the event.
488 Returns:
489 :class:`Event`
491 Raises:
492 InvalidCalendar: If the content is not valid according to :rfc:`5545`.
494 .. warning:: As time progresses, we will be stricter with the validation.
495 """
496 event: Event = super().new(
497 stamp=stamp if stamp is not None else cls._utc_now(),
498 created=created,
499 last_modified=last_modified,
500 comments=comments,
501 links=links,
502 related_to=related_to,
503 refids=refids,
504 concepts=concepts,
505 )
506 event.summary = summary
507 event.description = description
508 event.uid = uid if uid is not None else uuid.uuid4()
509 event.start = start
510 event.end = end
511 event.color = color
512 event.categories = categories
513 event.sequence = sequence
514 event.classification = classification
515 event.url = url
516 event.organizer = organizer
517 event.location = location
518 event.priority = priority
519 event.transparency = transparency
520 event.contacts = contacts
521 event.status = status
522 event.attendees = attendees
523 event.conferences = conferences
525 if cls._validate_new:
526 cls._validate_start_and_end(start, end)
527 return event
530__all__ = ["Event"]