1"""This implements the sub-component "AVAILABLE" of "VAVAILABILITY".
2
3This is specified in :rfc:`7953`.
4"""
5
6from __future__ import annotations
7
8import uuid
9from datetime import datetime
10from typing import TYPE_CHECKING
11
12from icalendar.attr import (
13 CONCEPTS_TYPE_SETTER,
14 LINKS_TYPE_SETTER,
15 RELATED_TO_TYPE_SETTER,
16 categories_property,
17 contacts_property,
18 description_property,
19 duration_property,
20 exdates_property,
21 location_property,
22 rdates_property,
23 rfc_7953_dtend_property,
24 rfc_7953_dtstart_property,
25 rfc_7953_duration_property,
26 rfc_7953_end_property,
27 rrules_property,
28 sequence_property,
29 summary_property,
30 uid_property,
31)
32from icalendar.cal.examples import get_example
33from icalendar.error import InvalidCalendar
34
35from .component import Component
36
37if TYPE_CHECKING:
38 from collections.abc import Sequence
39 from datetime import date
40
41
42class Available(Component):
43 """Sub-component of "VAVAILABILITY from :rfc:`7953`.
44
45 Description:
46 "AVAILABLE" subcomponents are used to indicate periods of free
47 time within the time range of the enclosing "VAVAILABILITY"
48 component. "AVAILABLE" subcomponents MAY include recurrence
49 properties to specify recurring periods of time, which can be
50 overridden using normal iCalendar recurrence behavior (i.e., use
51 of the "RECURRENCE-ID" property).
52
53 Examples:
54 This is a recurring "AVAILABLE" subcomponent:
55
56 .. code-block:: ics
57
58 BEGIN:AVAILABLE
59 UID:57DD4AAF-3835-46B5-8A39-B3B253157F01
60 SUMMARY:Monday to Friday from 9:00 to 17:00
61 DTSTART;TZID=America/Denver:20111023T090000
62 DTEND;TZID=America/Denver:20111023T170000
63 RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
64 LOCATION:Denver
65 END:AVAILABLE
66
67 You can get the same example from :meth:`example`:
68
69 .. code-block: pycon
70
71 >>> from icalendar import Available
72 >>> a = Available.example()
73 >>> str(a.summary)
74 'Monday to Friday from 9:00 to 17:00'
75
76 """
77
78 name = "AVAILABLE"
79
80 summary = summary_property
81 description = description_property
82 sequence = sequence_property
83 categories = categories_property
84 uid = uid_property
85 location = location_property
86 contacts = contacts_property
87 exdates = exdates_property
88 rdates = rdates_property
89 rrules = rrules_property
90 from icalendar.attr import RECURRENCE_ID
91
92 start = DTSTART = rfc_7953_dtstart_property
93 DTEND = rfc_7953_dtend_property
94 DURATION = duration_property("Available")
95 duration = rfc_7953_duration_property
96 end = rfc_7953_end_property
97
98 @classmethod
99 def new(
100 cls,
101 /,
102 categories: Sequence[str] = (),
103 comments: list[str] | str | None = None,
104 concepts: CONCEPTS_TYPE_SETTER = None,
105 contacts: list[str] | str | None = None,
106 created: date | None = None,
107 description: str | None = None,
108 end: datetime | None = None,
109 last_modified: date | None = None,
110 links: LINKS_TYPE_SETTER = None,
111 location: str | None = None,
112 recurrence_id: date | datetime | None = None,
113 refids: list[str] | str | None = None,
114 related_to: RELATED_TO_TYPE_SETTER = None,
115 sequence: int | None = None,
116 stamp: date | None = None,
117 start: datetime | None = None,
118 summary: str | None = None,
119 uid: str | uuid.UUID | None = None,
120 ):
121 """Create a new Available component with all required properties.
122
123 This creates a new Available component in accordance with :rfc:`7953`.
124
125 Parameters:
126 categories: The :attr:`categories` of the Available component.
127 comments: The :attr:`~icalendar.Component.comments` of the Available
128 component.
129 concepts: The :attr:`~icalendar.Component.concepts` of the Available
130 component.
131 contacts: The :attr:`contacts` of the Available component.
132 created: The :attr:`~icalendar.Component.created` of the Available
133 component.
134 description: The :attr:`description` of the Available component.
135 end: The :attr:`end` of the Available component.
136 last_modified: The :attr:`~icalendar.Component.last_modified` of the
137 Available component.
138 links: The :attr:`~icalendar.Component.links` of the Available component.
139 location: The :attr:`location` of the Available component.
140 recurrence_id: The :attr:`RECURRENCE_ID` of the Available component.
141 refids: :attr:`~icalendar.Component.refids` of the Available component.
142 related_to: :attr:`~icalendar.Component.related_to` of the Available
143 component.
144 sequence: The :attr:`sequence` of the Available component.
145 stamp: The :attr:`~icalendar.Component.stamp` of the Available component.
146 If None, this is set to the current time.
147 start: The :attr:`start` of the Available component.
148 summary: The :attr:`summary` of the Available component.
149 uid: The :attr:`uid` of the Available component.
150 If None, this is set to a new :func:`uuid.uuid4`.
151
152 Returns:
153 :class:`Available`
154
155 Raises:
156 ~error.InvalidCalendar: If the content is not valid
157 according to :rfc:`7953`.
158
159 .. warning:: As time progresses, we will be stricter with the validation.
160 """
161 available: Available = super().new(
162 stamp=stamp if stamp is not None else cls._utc_now(),
163 created=created,
164 last_modified=last_modified,
165 comments=comments,
166 links=links,
167 related_to=related_to,
168 refids=refids,
169 concepts=concepts,
170 )
171 available.summary = summary
172 available.description = description
173 available.uid = uid if uid is not None else uuid.uuid4()
174 available.sequence = sequence
175 available.categories = categories
176 available.location = location
177 available.contacts = contacts
178 available.RECURRENCE_ID = recurrence_id
179
180 if cls._validate_new:
181 if end is not None and (
182 not isinstance(end, datetime) or end.tzinfo is None
183 ):
184 raise InvalidCalendar(
185 "Available end must be a datetime with a timezone"
186 )
187 if not isinstance(start, datetime) or start.tzinfo is None:
188 raise InvalidCalendar(
189 "Available start must be a datetime with a timezone"
190 )
191 available._validate_start_and_end(start, end)
192 available.start = start
193 available.end = end
194 return available
195
196 @classmethod
197 def example(cls, name: str = "rfc_7953_1") -> Available:
198 """Return the calendar example with the given name."""
199 return cls.from_ical(get_example("availabilities", name)).available[0]
200
201
202__all__ = ["Available"]