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