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