Create VAVAILABLE and VAVAILABILITY classes

pull/861/head
Nicco Kunzmann 2025-06-02 10:39:49 +01:00
rodzic ef3f1c6e8d
commit 0a33b4c482
12 zmienionych plików z 399 dodań i 3 usunięć

Wyświetl plik

@ -17,10 +17,10 @@ This package is compatible with the following standards:
- :rfc:`6868` - Parameter Value Encoding in iCalendar and vCard
- :rfc:`7529` - Non-Gregorian Recurrence Rules in the Internet Calendaring and Scheduling Core Object Specification (iCalendar)
- :rfc:`9074` - "VALARM" Extensions for iCalendar
- :rfc:`7953` - Calendar Availability
We do not claim compatibility to the following RFCs. They might work though.
- :rfc:`7953` - Calendar Availability
- :rfc:`7986` - New Properties for iCalendar
- :rfc:`9073` - Event Publishing Extensions to iCalendar
- :rfc:`9253` - Support for iCalendar Relationships

Wyświetl plik

@ -4,6 +4,8 @@ from icalendar.alarms import (
)
from icalendar.cal import (
Alarm,
Availability,
Available,
Calendar,
Component,
ComponentFactory,
@ -76,6 +78,8 @@ __all__ = [
"Alarm",
"AlarmTime",
"Alarms",
"Availability",
"Available",
"Calendar",
"Component",
"ComponentEndMissing",

Wyświetl plik

@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Optional, Sequence, Union
from icalendar.error import InvalidCalendar
from icalendar.parser_tools import SEQUENCE_TYPES
from icalendar.prop import vCategory, vDDDTypes, vDuration, vRecur, vText
from icalendar.prop import vCalAddress, vCategory, vDDDTypes, vDuration, vRecur, vText
from icalendar.timezone import tzp
if TYPE_CHECKING:
@ -962,6 +962,48 @@ Examples:
""", # noqa: E501
)
def _get_organizer(self: Component) -> Optional[vCalAddress]:
"""ORGANIZER defines the organizer for a calendar component.
Property Parameters:
IANA, non-standard, language, common name,
directory entry reference, and sent-by property parameters can be
specified on this property.
Conformance:
This property MUST be specified in an iCalendar object
that specifies a group-scheduled calendar entity. This property
MUST be specified in an iCalendar object that specifies the
publication of a calendar user's busy time. This property MUST
NOT be specified in an iCalendar object that specifies only a time
zone definition or that defines calendar components that are not
group-scheduled components, but are components only on a single
user's calendar.
Description:
This property is specified within the "VEVENT",
"VTODO", and "VJOURNAL" calendar components to specify the
organizer of a group-scheduled calendar entity. The property is
specified within the "VFREEBUSY" calendar component to specify the
calendar user requesting the free or busy time. When publishing a
"VFREEBUSY" calendar component, the property is used to specify
the calendar that the published busy time came from.
The property has the property parameters "CN", for specifying the
common or display name associated with the "Organizer", "DIR", for
specifying a pointer to the directory information associated with
the "Organizer", "SENT-BY", for specifying another calendar user
that is acting on behalf of the "Organizer". The non-standard
parameters may also be specified on this property. If the
"LANGUAGE" property parameter is specified, the identified
language applies to the "CN" parameter value.
"""
return self.get("ORGANIZER")
organizer_property = property(_get_organizer)
__all__ = [
"categories_property",
"color_property",
@ -970,6 +1012,7 @@ __all__ = [
"descriptions_property",
"exdates_property",
"multi_language_text_property",
"organizer_property",
"property_del_duration",
"property_doc_duration_template",
"property_get_duration",

Wyświetl plik

@ -5,6 +5,8 @@ These are the defined components.
"""
from .alarm import Alarm
from .avaiable import Available
from .availability import Availability
from .calendar import Calendar
from .component import Component
from .component_factory import ComponentFactory
@ -16,6 +18,8 @@ from .todo import Todo
__all__ = [
"Alarm",
"Availability",
"Available",
"Calendar",
"Component",
"ComponentFactory",

Wyświetl plik

@ -0,0 +1,38 @@
"""This implements the sub-component "AVAILABLE" of "VAVAILABILITY".
This is specified in :rfc:`7953`.
"""
from .component import Component
class Available(Component):
"""Sub-component of "VAVAILABILITY from :rfc:`7953`.
Description:
"AVAILABLE" subcomponents are used to indicate periods of free
time within the time range of the enclosing "VAVAILABILITY"
component. "AVAILABLE" subcomponents MAY include recurrence
properties to specify recurring periods of time, which can be
overridden using normal iCalendar recurrence behavior (i.e., use
of the "RECURRENCE-ID" property).
Examples:
This is a recurring "AVAILABLE" subcomponent:
.. code-block:: text
BEGIN:AVAILABLE
UID:57DD4AAF-3835-46B5-8A39-B3B253157F01
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART;TZID=America/Denver:20111023T090000
DTEND;TZID=America/Denver:20111023T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
LOCATION:Denver
END:AVAILABLE
"""
name = "VAVAILABLE"
__all__ = ["Available"]

Wyświetl plik

@ -0,0 +1,184 @@
"""This implementes the VAVAILABILITY component.
This is specified in :rfc:`7953`.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from icalendar.attr import organizer_property
from .component import Component
if TYPE_CHECKING:
from icalendar.cal.avaiable import Available
class Availability(Component):
"""VAVAILABILITY component from :rfc:`7953`.
This provides a grouping of component properties and
subcomponents that describe the availability associated with a
calendar user.
Description:
A "VAVAILABILITY" component indicates a period of time
within which availability information is provided. A
"VAVAILABILITY" component can specify a start time and an end time
or duration. If "DTSTART" is not present, then the start time is
unbounded. If "DTEND" or "DURATION" are not present, then the end
time is unbounded. Within the specified time period, availability
defaults to a free-busy type of "BUSY-UNAVAILABLE" (see
Section 3.2), except for any time periods corresponding to
"AVAILABLE" subcomponents.
"AVAILABLE" subcomponents are used to indicate periods of free
time within the time range of the enclosing "VAVAILABILITY"
component. "AVAILABLE" subcomponents MAY include recurrence
properties to specify recurring periods of time, which can be
overridden using normal iCalendar recurrence behavior (i.e., use
of the "RECURRENCE-ID" property).
If specified, the "DTSTART" and "DTEND" properties in
"VAVAILABILITY" components and "AVAILABLE" subcomponents MUST be
"DATE-TIME" values specified as either the date with UTC time or
the date with local time and a time zone reference.
The iCalendar object containing the "VAVAILABILITY" component MUST
contain appropriate "VTIMEZONE" components corresponding to each
unique "TZID" parameter value used in any DATE-TIME properties in
all components, unless [RFC7809] is in effect.
When used to publish available time, the "ORGANIZER" property
specifies the calendar user associated with the published
available time.
If the "PRIORITY" property is specified in "VAVAILABILITY"
components, it is used to determine how that component is combined
with other "VAVAILABILITY" components. See Section 4.
Other calendar properties MAY be specified in "VAVAILABILITY" or
"AVAILABLE" components and are considered attributes of the marked
block of time. Their usage is application specific. For example,
the "LOCATION" property might be used to indicate that a person is
available in one location for part of the week and a different
location for another part of the week (but see Section 9 for when
it is appropriate to add additional data like this).
Example:
The following is an example of a "VAVAILABILITY" calendar
component used to represent the availability of a user, always
available Monday through Friday, 9:00 am to 5:00 pm in the
America/Montreal time zone:
.. code-block:: text
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:0428C7D2-688E-4D2E-AC52-CD112E2469DF
DTSTAMP:20111005T133225Z
BEGIN:AVAILABLE
UID:34EDA59B-6BB1-4E94-A66C-64999089C0AF
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
END:AVAILABLE
END:VAVAILABILITY
The following is an example of a "VAVAILABILITY" calendar
component used to represent the availability of a user available
Monday through Thursday, 9:00 am to 5:00 pm, at the main office,
and Friday, 9:00 am to 12:00 pm, in the branch office in the
America/Montreal time zone between October 2nd and December 2nd
2011:
.. code-block:: text
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:84D0F948-7FC6-4C1D-BBF3-BA9827B424B5
DTSTAMP:20111005T133225Z
DTSTART;TZID=America/Montreal:20111002T000000
DTEND;TZID=America/Montreal:20111202T000000
BEGIN:AVAILABLE
UID:7B33093A-7F98-4EED-B381-A5652530F04D
SUMMARY:Monday to Thursday from 9:00 to 17:00
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH
LOCATION:Main Office
END:AVAILABLE
BEGIN:AVAILABLE
UID:DF39DC9E-D8C3-492F-9101-0434E8FC1896
SUMMARY:Friday from 9:00 to 12:00
DTSTART;TZID=America/Montreal:20111006T090000
DTEND;TZID=America/Montreal:20111006T120000
RRULE:FREQ=WEEKLY
LOCATION:Branch Office
END:AVAILABLE
END:VAVAILABILITY
For more examples, have a look at :rfc:`5545`.
"""
name = "VAVAILABILITY"
canonical_order = (
"DTSTART",
"DTEND",
"DURATION",
"DTSTAMP",
"UID",
"SEQUENCE",
"SUMMARY",
"DESCRIPTION",
"ORGANIZER",
)
required = (
"DTSTART",
"DTSTAMP",
"UID",
)
singletons = (
"DTSTAMP",
"UID",
"BUSYTYPE",
"CLASS",
"CREATED",
"DESCRIPTION",
"DTSTART",
"LAST-MODIFIED",
"LOCATION",
"ORGANIZER",
"PRIORITY",
"SEQUENCE",
"SUMMARY",
"URL",
"DTEND",
"DURATION",
)
exclusive = (
"DTEND",
"DURATION",
)
organizer = organizer_property
@property
def available(self) -> list[Available]:
"""All event components in the calendar.
This is a shortcut to get all VAVAILABLE sub-components.
Modifications do not change the calendar.
Use :py:meth:`Component.add_component`.
"""
return self.walk("VAVAILABLE")
__all__ = ["Availability"]

Wyświetl plik

@ -14,6 +14,8 @@ class ComponentFactory(CaselessDict):
"""Set keys to upper for initial dict."""
super().__init__(*args, **kwargs)
from icalendar.cal.alarm import Alarm
from icalendar.cal.avaiable import Available
from icalendar.cal.availability import Availability
from icalendar.cal.calendar import Calendar
from icalendar.cal.event import Event
from icalendar.cal.free_busy import FreeBusy
@ -30,6 +32,8 @@ class ComponentFactory(CaselessDict):
self["DAYLIGHT"] = TimezoneDaylight
self["VALARM"] = Alarm
self["VCALENDAR"] = Calendar
self["AVAILABLE"] = Available
self["VAVAILABILITY"] = Availability
__all__ = ["ComponentFactory"]

Wyświetl plik

@ -0,0 +1,12 @@
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:0428C7D2-688E-4D2E-AC52-CD112E2469DF
DTSTAMP:20111005T133225Z
BEGIN:AVAILABLE
UID:34EDA59B-6BB1-4E94-A66C-64999089C0AF
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
END:AVAILABLE
END:VAVAILABILITY

Wyświetl plik

@ -0,0 +1,23 @@
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:84D0F948-7FC6-4C1D-BBF3-BA9827B424B5
DTSTAMP:20111005T133225Z
DTSTART;TZID=America/Montreal:20111002T000000
DTEND;TZID=America/Montreal:20111202T000000
BEGIN:AVAILABLE
UID:7B33093A-7F98-4EED-B381-A5652530F04D
SUMMARY:Monday to Thursday from 9:00 to 17:00
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH
LOCATION:Main Office
END:AVAILABLE
BEGIN:AVAILABLE
UID:DF39DC9E-D8C3-492F-9101-0434E8FC1896
SUMMARY:Friday from 9:00 to 12:00
DTSTART;TZID=America/Montreal:20111006T090000
DTEND;TZID=America/Montreal:20111006T120000
RRULE:FREQ=WEEKLY
LOCATION:Branch Office
END:AVAILABLE
END:VAVAILABILITY

Wyświetl plik

@ -0,0 +1,44 @@
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:BE082249-7BDD-4FE0-BDBA-DE6598C32FC9
DTSTAMP:20111005T133225Z
DTSTART;TZID=America/Montreal:20111002T000000
DTEND;TZID=America/Montreal:20111023T030000
BEGIN:AVAILABLE
UID:54602321-CEDB-4620-9099-757583263981
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
LOCATION:Montreal
END:AVAILABLE
END:VAVAILABILITY
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:A1FF55E3-555C-433A-8548-BF4864B5621E
DTSTAMP:20111005T133225Z
DTSTART;TZID=America/Denver:20111023T000000
DTEND;TZID=America/Denver:20111030T000000
BEGIN:AVAILABLE
UID:57DD4AAF-3835-46B5-8A39-B3B253157F01
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART;TZID=America/Denver:20111023T090000
DTEND;TZID=America/Denver:20111023T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
LOCATION:Denver
END:AVAILABLE
END:VAVAILABILITY
BEGIN:VAVAILABILITY
ORGANIZER:mailto:bernard@example.com
UID:1852F9E1-E0AA-4572-B4C4-ED1680A4DA40
DTSTAMP:20111005T133225Z
DTSTART;TZID=America/Montreal:20111030T030000
BEGIN:AVAILABLE
UID:D27C421F-16C2-4ECB-8352-C45CA352C72A
SUMMARY:Monday to Friday from 9:00 to 17:00
DTSTART;TZID=America/Montreal:20111030T090000
DTEND;TZID=America/Montreal:20111030T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
LOCATION:Montreal
END:AVAILABLE
END:VAVAILABILITY

Wyświetl plik

@ -5,7 +5,6 @@ except ImportError:
from datetime import datetime, timezone
from typing import Generator
from icalendar import TypesFactory
import pytest
import icalendar
@ -14,6 +13,7 @@ import icalendar.cal.calendar
import icalendar.cal.component_factory
import icalendar.cal.event
import icalendar.cal.timezone
from icalendar import TypesFactory
from icalendar.cal.component import Component
from . import timezone_ids
@ -108,6 +108,7 @@ CALENDARS_FOLDER = HERE / "calendars"
TIMEZONES_FOLDER = HERE / "timezones"
EVENTS_FOLDER = HERE / "events"
ALARMS_FOLDER = HERE / "alarms"
AVAILABILITIES_FOLDER = HERE / "availabilities"
@pytest.fixture(scope="module")
@ -130,6 +131,11 @@ def alarms(tzp):
return DataSource(ALARMS_FOLDER, icalendar.cal.alarm.Alarm.from_ical)
@pytest.fixture(scope="module")
def availabilities(tzp):
return DataSource(AVAILABILITIES_FOLDER, icalendar.cal.alarm.Alarm.from_ical)
@pytest.fixture(params=PYTZ_UTC + [zoneinfo.ZoneInfo("UTC"), tz.UTC, tz.gettz("UTC")])
def utc(request, tzp):
return request.param
@ -190,6 +196,7 @@ def types_factory():
"""Return a new types factory."""
return TypesFactory()
@pytest.fixture
def x_sometime(types_factory):
"""Map x_sometime to time"""

Wyświetl plik

@ -0,0 +1,33 @@
"""This tests the parsing of the VAVAILABILITY component as defined in :rfc:`7953`."""
from datetime import datetime
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from icalendar import Availability
def test_uid(availabilities):
"""Test the UID property."""
availability: Availability = availabilities.rfc_7953_1
assert availability.uid == "0428C7D2-688E-4D2E-AC52-CD112E2469DF"
def test_organizer(availabilities):
"""Test the ORGANIZER property."""
availability: Availability = availabilities.rfc_7953_1
assert availability.organizer == "mailto:bernard@example.com"
assert availability.organizer.email == "bernard@example.com"
def test_dtstamp(availabilities, tzp):
"""Test the DTSTAMP property."""
availability: Availability = availabilities.rfc_7953_1
assert availability.DTSTAMP == tzp.localize_utc(datetime(2011, 10, 5, 13, 32, 25))
def test_subcomponents(availabilities):
"""Test the subcomponents."""
availability: Availability = availabilities.rfc_7953_1
assert len(availability.subcomponents) == 1
assert availability.available == availability.subcomponents