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.
306 You can set the duration to automatically adjust the end time while keeping
307 start locked.
309 Setting the duration will:
311 1. Keep the start time locked (unchanged)
312 2. Adjust the end time to start + duration
313 3. Remove any existing DTEND property
314 4. Set the DURATION property
315 """
316 return get_duration_property(self)
318 @duration.setter
319 def duration(self, value: timedelta):
320 if not isinstance(value, timedelta):
321 raise TypeError(f"Use timedelta, not {type(value).__name__}.")
323 # Use the set_duration method with default start-locked behavior
324 self.set_duration(value, locked="start")
326 @property
327 def start(self) -> date | datetime:
328 """The start of the event.
330 Invalid values raise an InvalidCalendar.
331 If there is no start, we also raise an IncompleteComponent error.
333 You can get the start, end and duration of an event as follows:
335 >>> from datetime import datetime
336 >>> from icalendar import Event
337 >>> event = Event()
338 >>> event.start = datetime(2021, 1, 1, 12)
339 >>> event.end = datetime(2021, 1, 1, 12, 30) # 30 minutes
340 >>> event.duration # 1800 seconds == 30 minutes
341 datetime.timedelta(seconds=1800)
342 >>> print(event.to_ical())
343 BEGIN:VEVENT
344 DTSTART:20210101T120000
345 DTEND:20210101T123000
346 END:VEVENT
347 """
348 return get_start_property(self)
350 @start.setter
351 def start(self, start: date | datetime | None):
352 """Set the start."""
353 self.DTSTART = start
355 @property
356 def end(self) -> date | datetime:
357 """The end of the event.
359 Invalid values raise an InvalidCalendar error.
360 If there is no end, we also raise an IncompleteComponent error.
361 """
362 return get_end_property(self, "DTEND")
364 @end.setter
365 def end(self, end: date | datetime | None):
366 """Set the end."""
367 self.DTEND = end
369 def set_duration(
370 self, duration: timedelta | None, locked: Literal["start", "end"] = "start"
371 ):
372 """Set the duration of the event relative to either start or end.
374 Args:
375 duration: The duration to set, or None to convert to DURATION property
376 locked: Which property to keep unchanged ('start' or 'end')
377 """
378 set_duration_with_locking(self, duration, locked, "DTEND")
380 def set_start(
381 self, start: date | datetime, locked: Literal["duration", "end"] | None = None
382 ):
383 """Set the start and keep the duration or end of the event.
385 Args:
386 start: The start time to set
387 locked: Which property to keep unchanged ('duration', 'end', or None
388 for auto-detect)
389 """
390 set_start_with_locking(self, start, locked, "DTEND")
392 def set_end(
393 self, end: date | datetime, locked: Literal["start", "duration"] = "start"
394 ):
395 """Set the end of the component, keeping either the start or the duration same.
397 Args:
398 end: The end time to set
399 locked: Which property to keep unchanged ('start' or 'duration')
400 """
401 set_end_with_locking(self, end, locked, "DTEND")
403 X_MOZ_SNOOZE_TIME = X_MOZ_SNOOZE_TIME_property
404 X_MOZ_LASTACK = X_MOZ_LASTACK_property
405 color = color_property
406 sequence = sequence_property
407 categories = categories_property
408 rdates = rdates_property
409 exdates = exdates_property
410 rrules = rrules_property
411 uid = uid_property
412 summary = summary_property
413 description = description_property
414 classification = class_property
415 url = url_property
416 organizer = organizer_property
417 location = location_property
418 priority = priority_property
419 contacts = contacts_property
420 transparency = transparency_property
421 status = status_property
422 attendees = attendees_property
423 images = images_property
424 conferences = conferences_property
426 @classmethod
427 def new(
428 cls,
429 /,
430 attendees: list[vCalAddress] | None = None,
431 categories: Sequence[str] = (),
432 classification: CLASS | None = None,
433 color: str | None = None,
434 comments: list[str] | str | None = None,
435 concepts: CONCEPTS_TYPE_SETTER = None,
436 conferences: list[Conference] | None = None,
437 contacts: list[str] | str | None = None,
438 created: date | None = None,
439 description: str | None = None,
440 end: date | datetime | None = None,
441 last_modified: date | None = None,
442 links: LINKS_TYPE_SETTER = None,
443 location: str | None = None,
444 organizer: vCalAddress | str | None = None,
445 priority: int | None = None,
446 refids: list[str] | str | None = None,
447 related_to: RELATED_TO_TYPE_SETTER = None,
448 sequence: int | None = None,
449 stamp: date | None = None,
450 start: date | datetime | None = None,
451 status: STATUS | None = None,
452 transparency: TRANSP | None = None,
453 summary: str | None = None,
454 uid: str | uuid.UUID | None = None,
455 url: str | None = None,
456 ):
457 """Create a new event with all required properties.
459 This creates a new Event in accordance with :rfc:`5545`.
461 Arguments:
462 attendees: The :attr:`attendees` of the event.
463 categories: The :attr:`categories` of the event.
464 classification: The :attr:`classification` of the event.
465 color: The :attr:`color` of the event.
466 comments: The :attr:`~icalendar.Component.comments` of the event.
467 concepts: The :attr:`~icalendar.Component.concepts` of the event.
468 conferences: The :attr:`conferences` of the event.
469 created: The :attr:`~icalendar.Component.created` of the event.
470 description: The :attr:`description` of the event.
471 end: The :attr:`end` of the event.
472 last_modified: The :attr:`~icalendar.Component.last_modified` of the event.
473 links: The :attr:`~icalendar.Component.links` of the event.
474 location: The :attr:`location` of the event.
475 organizer: The :attr:`organizer` of the event.
476 priority: The :attr:`priority` of the event.
477 refids: :attr:`~icalendar.Component.refids` of the event.
478 related_to: :attr:`~icalendar.Component.related_to` of the event.
479 sequence: The :attr:`sequence` of the event.
480 stamp: The :attr:`~icalendar.Component.stamp` of the event.
481 If None, this is set to the current time.
482 start: The :attr:`start` of the event.
483 status: The :attr:`status` of the event.
484 summary: The :attr:`summary` of the event.
485 transparency: The :attr:`transparency` of the event.
486 uid: The :attr:`uid` of the event.
487 If None, this is set to a new :func:`uuid.uuid4`.
488 url: The :attr:`url` of the event.
490 Returns:
491 :class:`Event`
493 Raises:
494 ~error.InvalidCalendar: If the content is not valid according to :rfc:`5545`.
496 .. warning:: As time progresses, we will be stricter with the validation.
497 """
498 event: Event = super().new(
499 stamp=stamp if stamp is not None else cls._utc_now(),
500 created=created,
501 last_modified=last_modified,
502 comments=comments,
503 links=links,
504 related_to=related_to,
505 refids=refids,
506 concepts=concepts,
507 )
508 event.summary = summary
509 event.description = description
510 event.uid = uid if uid is not None else uuid.uuid4()
511 event.start = start
512 event.end = end
513 event.color = color
514 event.categories = categories
515 event.sequence = sequence
516 event.classification = classification
517 event.url = url
518 event.organizer = organizer
519 event.location = location
520 event.priority = priority
521 event.transparency = transparency
522 event.contacts = contacts
523 event.status = status
524 event.attendees = attendees
525 event.conferences = conferences
527 if cls._validate_new:
528 cls._validate_start_and_end(start, end)
529 return event
532__all__ = ["Event"]