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 """
191 name = "VEVENT"
193 canonical_order = (
194 "SUMMARY",
195 "DTSTART",
196 "DTEND",
197 "DURATION",
198 "DTSTAMP",
199 "UID",
200 "RECURRENCE-ID",
201 "SEQUENCE",
202 "RRULE",
203 "RDATE",
204 "EXDATE",
205 )
207 required = (
208 "UID",
209 "DTSTAMP",
210 )
211 singletons = (
212 "CLASS",
213 "CREATED",
214 "COLOR",
215 "DESCRIPTION",
216 "DTSTART",
217 "GEO",
218 "LAST-MODIFIED",
219 "LOCATION",
220 "ORGANIZER",
221 "PRIORITY",
222 "DTSTAMP",
223 "SEQUENCE",
224 "STATUS",
225 "SUMMARY",
226 "TRANSP",
227 "URL",
228 "RECURRENCE-ID",
229 "DTEND",
230 "DURATION",
231 "UID",
232 )
233 exclusive = (
234 "DTEND",
235 "DURATION",
236 )
237 multiple = (
238 "ATTACH",
239 "ATTENDEE",
240 "CATEGORIES",
241 "COMMENT",
242 "CONTACT",
243 "EXDATE",
244 "REQUEST-STATUS",
245 "RELATED",
246 "RESOURCES",
247 "RDATE",
248 "RRULE",
249 )
250 ignore_exceptions = True
252 @property
253 def alarms(self) -> Alarms:
254 """Compute the alarm times for this component.
256 >>> from icalendar import Event
257 >>> event = Event.example("rfc_9074_example_1")
258 >>> len(event.alarms.times)
259 1
260 >>> alarm_time = event.alarms.times[0]
261 >>> alarm_time.trigger # The time when the alarm pops up
262 datetime.datetime(2021, 3, 2, 10, 15, tzinfo=ZoneInfo(key='America/New_York'))
263 >>> alarm_time.is_active() # This alarm has not been acknowledged
264 True
266 Note that this only uses DTSTART and DTEND, but ignores
267 RDATE, EXDATE, and RRULE properties.
268 """
269 from icalendar.alarms import Alarms
271 return Alarms(self)
273 @classmethod
274 def example(cls, name: str = "rfc_9074_example_3") -> Event:
275 """Return the calendar example with the given name."""
276 return cls.from_ical(get_example("events", name))
278 DTSTART = create_single_property(
279 "DTSTART",
280 "dt",
281 (datetime, date),
282 date,
283 'The "DTSTART" property for a "VEVENT" specifies the inclusive start of the event.',
284 )
285 DTEND = create_single_property(
286 "DTEND",
287 "dt",
288 (datetime, date),
289 date,
290 'The "DTEND" property for a "VEVENT" calendar component specifies the non-inclusive end of the event.',
291 )
293 def _get_start_end_duration(self):
294 """Verify the calendar validity and return the right attributes."""
295 return get_start_end_duration_with_validation(
296 self, "DTSTART", "DTEND", "VEVENT"
297 )
299 DURATION = property(
300 property_get_duration,
301 property_set_duration,
302 property_del_duration,
303 property_doc_duration_template.format(component="VEVENT"),
304 )
306 @property
307 def duration(self) -> timedelta:
308 """The duration of the VEVENT.
310 Returns the DURATION property if set, otherwise calculated from start and end.
311 When setting duration, the end time is automatically calculated from start +
312 duration.
314 You can set the duration to automatically adjust the end time while keeping
315 start locked.
317 Setting the duration will do the following.
319 1. Keep the start time locked (unchanged)
320 2. Adjust the end time to start + duration
321 3. Remove any existing DTEND property
322 4. Set the DURATION property
323 """
324 return get_duration_property(self)
326 @duration.setter
327 def duration(self, value: timedelta):
328 if not isinstance(value, timedelta):
329 raise TypeError(f"Use timedelta, not {type(value).__name__}.")
331 # Use the set_duration method with default start-locked behavior
332 self.set_duration(value, locked="start")
334 @property
335 def start(self) -> date | datetime:
336 """The start of the event.
338 Invalid values raise an :exc:`~icalendar.error.InvalidCalendar`.
339 If there is no start, we also raise an :exc:`~icalendar.error.IncompleteComponent` error.
341 You can get the start, end and duration of an event as follows:
343 >>> from datetime import datetime
344 >>> from icalendar import Event
345 >>> event = Event()
346 >>> event.start = datetime(2021, 1, 1, 12)
347 >>> event.end = datetime(2021, 1, 1, 12, 30) # 30 minutes
348 >>> event.duration # 1800 seconds == 30 minutes
349 datetime.timedelta(seconds=1800)
350 >>> print(event.to_ical())
351 BEGIN:VEVENT
352 DTSTART:20210101T120000
353 DTEND:20210101T123000
354 END:VEVENT
355 """
356 return get_start_property(self)
358 @start.setter
359 def start(self, start: date | datetime | None):
360 """Set the start."""
361 self.DTSTART = start
363 @property
364 def end(self) -> date | datetime:
365 """The end of the event.
367 Invalid values raise an :exc:`~icalendar.error.InvalidCalendar` error.
368 If there is no end, we also raise an :exc:`~icalendar.error.IncompleteComponent` error.
369 """
370 return get_end_property(self, "DTEND")
372 @end.setter
373 def end(self, end: date | datetime | None):
374 """Set the end."""
375 self.DTEND = end
377 def set_duration(
378 self, duration: timedelta | None, locked: Literal["start", "end"] = "start"
379 ):
380 """Set the duration of the event relative to either start or end.
382 Parameters:
383 duration: The duration to set, or None to convert to DURATION property
384 locked: Which property to keep unchanged ('start' or 'end')
385 """
386 set_duration_with_locking(self, duration, locked, "DTEND")
388 def set_start(
389 self, start: date | datetime, locked: Literal["duration", "end"] | None = None
390 ):
391 """Set the start and keep the duration or end of the event.
393 Parameters:
394 start: The start time to set
395 locked: Which property to keep unchanged ('duration', 'end', or None
396 for auto-detect)
397 """
398 set_start_with_locking(self, start, locked, "DTEND")
400 def set_end(
401 self, end: date | datetime, locked: Literal["start", "duration"] = "start"
402 ):
403 """Set the end of the component, keeping either the start or the duration same.
405 Parameters:
406 end: The end time to set
407 locked: Which property to keep unchanged ('start' or 'duration')
408 """
409 set_end_with_locking(self, end, locked, "DTEND")
411 X_MOZ_SNOOZE_TIME = X_MOZ_SNOOZE_TIME_property
412 X_MOZ_LASTACK = X_MOZ_LASTACK_property
413 color = color_property
414 sequence = sequence_property
415 categories = categories_property
416 rdates = rdates_property
417 exdates = exdates_property
418 rrules = rrules_property
419 REQUEST_STATUS = REQUEST_STATUS_property
420 RESOURCES = RESOURCES_property
421 uid = uid_property
422 summary = summary_property
423 description = description_property
424 classification = class_property
425 url = url_property
426 organizer = organizer_property
427 location = location_property
428 priority = priority_property
429 contacts = contacts_property
430 transparency = transparency_property
431 status = status_property
432 attendees = attendees_property
433 attachments = attachments_property
434 images = images_property
435 conferences = conferences_property
436 from icalendar.attr import RECURRENCE_ID
438 @classmethod
439 def new(
440 cls,
441 /,
442 attachments: ATTACHMENTS_TYPE_SETTER = None,
443 attendees: ATTENDEE_TYPE_SETTER = None,
444 categories: Sequence[str] = (),
445 classification: CLASS | None = None,
446 color: str | None = None,
447 comments: list[str] | str | None = None,
448 concepts: CONCEPTS_TYPE_SETTER = None,
449 conferences: list[Conference] | None = None,
450 contacts: list[str] | str | None = None,
451 created: date | None = None,
452 description: str | None = None,
453 end: date | datetime | None = None,
454 last_modified: date | None = None,
455 links: LINKS_TYPE_SETTER = None,
456 location: str | None = None,
457 organizer: vCalAddress | str | None = None,
458 priority: int | None = None,
459 recurrence_id: date | datetime | None = None,
460 refids: list[str] | str | None = None,
461 related_to: RELATED_TO_TYPE_SETTER = None,
462 request_status: list[str] | str | None = None,
463 resources: list[str] | str | None = None,
464 sequence: int | None = None,
465 stamp: date | None = None,
466 start: date | datetime | None = None,
467 status: STATUS | None = None,
468 subcomponents: Iterable[Component] | None = None,
469 transparency: TRANSP | None = None,
470 summary: str | None = None,
471 uid: str | uuid.UUID | None = None,
472 url: str | None = None,
473 ) -> Self:
474 """Create a new event with the required properties of ``stamp`` and ``uid``.
476 This creates a new ``Event`` in accordance with :rfc:`5545#section-3.6.1`.
478 Parameters:
479 attachments: The :attr:`attachments` of the event.
480 attendees: The :attr:`attendees` of the event.
481 categories: The :attr:`categories` of the event.
482 classification: The :attr:`classification` of the event.
483 color: The :attr:`color` of the event.
484 comments: The :attr:`~icalendar.Component.comments` of the event.
485 concepts: The :attr:`~icalendar.Component.concepts` of the event.
486 conferences: The :attr:`conferences` of the event.
487 created: The :attr:`~icalendar.Component.created` of the event.
488 description: The :attr:`description` of the event.
489 end: The :attr:`end` of the event.
490 last_modified: The :attr:`~icalendar.Component.last_modified` of the event.
491 links: The :attr:`~icalendar.Component.links` of the event.
492 location: The :attr:`location` of the event.
493 organizer: The :attr:`organizer` of the event.
494 priority: The :attr:`priority` of the event.
495 recurrence_id: The :attr:`RECURRENCE_ID` of the event.
496 refids: :attr:`~icalendar.Component.refids` of the event.
497 related_to: :attr:`~icalendar.Component.related_to` of the event.
498 request_status: The :attr:`REQUEST_STATUS` of the event.
499 resources: The :attr:`RESOURCES` of the event.
500 sequence: The :attr:`sequence` of the event.
501 stamp: The :attr:`~icalendar.Component.stamp` of the event.
502 If ``None``, this is set to the current UTC time.
503 start: The :attr:`start` of the event.
504 status: The :attr:`status` of the event.
505 subcomponents: The subcomponents of the event.
506 summary: The :attr:`summary` of the event.
507 transparency: The :attr:`transparency` of the event.
508 uid: The :attr:`uid` of the event.
509 If ``None``, this is set to a new :func:`uuid.uuid4`.
510 url: The :attr:`url` of the event.
512 Returns:
513 :class:`Event`
515 Raises:
516 :exc:`~icalendar.error.InvalidCalendar`: If the content is not valid
517 according to :rfc:`5545`.
519 .. warning:: As time progresses, we will be stricter with the validation.
520 """
521 event: Self = super().new(
522 stamp=stamp if stamp is not None else cls._utc_now(),
523 created=created,
524 last_modified=last_modified,
525 comments=comments,
526 links=links,
527 related_to=related_to,
528 refids=refids,
529 concepts=concepts,
530 subcomponents=subcomponents,
531 )
532 event.summary = summary
533 event.description = description
534 event.uid = uid if uid is not None else uuid.uuid4()
535 event.start = start
536 event.end = end
537 event.color = color
538 event.categories = categories
539 event.sequence = sequence
540 event.classification = classification
541 event.url = url
542 event.organizer = organizer
543 event.location = location
544 event.priority = priority
545 event.transparency = transparency
546 event.attachments = attachments
547 event.contacts = contacts
548 event.status = status
549 event.REQUEST_STATUS = request_status
550 event.RESOURCES = resources
551 event.attendees = attendees
552 event.conferences = conferences
553 event.RECURRENCE_ID = recurrence_id
555 if cls._validate_new:
556 cls._validate_start_and_end(start, end)
557 return event
560__all__ = ["Event"]