Add VCALENDAR properties NAME, DESCRIPTION and COLOR as attributes

pull/801/head
Nicco Kunzmann 2025-04-18 18:16:37 +01:00
rodzic 8add6bfe64
commit 800e990d70
3 zmienionych plików z 260 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,40 @@
"""Attributes of Components and properties."""
from __future__ import annotations
from typing import Optional
def multi_language_text_property(main_prop:str, compatibility_prop:str, doc:str) -> property:
"""This creates a text property.
This property can be defined several times with different LANGAUGE parameters.
Args:
main_prop: The property to set and get, e.g. NAME
compatibility_prop: An old property used before, e.g. X-WR-CALNAME
doc: The documentation string
"""
def fget(self) -> Optional[str]:
"""Get the property"""
result = self.get(main_prop, self.get(compatibility_prop))
if isinstance(result, list):
for item in result:
if "LANGUAGE" not in item.params:
return item
return result
def fset(self, value:str):
"""Set the property."""
fdel(self)
self.add(main_prop, value)
def fdel(self):
"""Delete the property."""
self.pop(main_prop, None)
self.pop(compatibility_prop, None)
return property(fget, fset, fdel, doc)
__all__ = ["multi_language_text_property"]

Wyświetl plik

@ -16,6 +16,7 @@ from typing import TYPE_CHECKING, List, NamedTuple, Optional, Tuple, Union
import dateutil.rrule
import dateutil.tz
from icalendar.attr import multi_language_text_property
from icalendar.caselessdict import CaselessDict
from icalendar.parser import Contentline, Contentlines, Parameters, q_join, q_split
from icalendar.parser_tools import DEFAULT_ENCODING
@ -1898,6 +1899,10 @@ class Calendar(Component):
"PRODID",
"CALSCALE",
"METHOD",
"NAME",
"X-WR-CALNAME",
"DESCRIPTION",
"X-WR-CALDESC",
)
required = (
"PRODID",
@ -2055,6 +2060,107 @@ class Calendar(Component):
continue
self.add_component(timezone)
calendar_name = multi_language_text_property(
"NAME", "X-WR-CALNAME",
"""This property specifies the name of the calendar
This takes care of :rfc:`7986` ``NAME`` and ``X-WR-CALNAME``.
Property Parameters:
IANA, non-standard, alternate text
representation, and language property parameters can be specified
on this property.
Conformance:
This property can be specified multiple times in an
iCalendar object. However, each property MUST represent the name
of the calendar in a different language.
Description:
This property is used to specify a name of the
iCalendar object that can be used by calendar user agents when
presenting the calendar data to a user. Whilst a calendar only
has a single name, multiple language variants can be specified by
including this property multiple times with different "LANGUAGE"
parameter values on each.
>>> from icalendar import Calendar
>>> calendar = Calendar()
>>> calendar.calendar_name = "My Calendar"
>>> print(calendar.to_ical())
BEGIN:VCALENDAR
NAME:My Calendar
END:VCALENDAR
""")
description = calendar_description = multi_language_text_property(
"DESCRIPTION", "X-WR-CALDESC",
"""This property specifies the description of the calendar.
This takes care of :rfc:`7986` ``DESCRIPTION`` and ``X-WR-CALDESC``.
Conformance:
This property can be specified multiple times in an
iCalendar object. However, each property MUST represent the
description of the calendar in a different language.
Description:
This property is used to specify a lengthy textual
description of the iCalendar object that can be used by calendar
user agents when describing the nature of the calendar data to a
user. Whilst a calendar only has a single description, multiple
language variants can be specified by including this property
multiple times with different "LANGUAGE" parameter values on each.
>>> from icalendar import Calendar
>>> calendar = Calendar()
>>> calendar.description = "This is a calendar"
>>> print(calendar.to_ical())
BEGIN:VCALENDAR
DESCRIPTION:This is a calendar
END:VCALENDAR
""")
color = calendar_color = multi_language_text_property(
"COLOR", "X-APPLE-CALENDAR-COLOR",
"""This property specifies a color used for displaying the calendar.
This takes care of :rfc:`7986` ``COLOR`` and ``X-APPLE-CALENDAR-COLOR``.
Property Parameters:
IANA and non-standard property parameters can
be specified on this property.
Conformance:
This property can be specified once in an iCalendar
object or in "VEVENT", "VTODO", or "VJOURNAL" calendar components.
Description:
This property specifies a color that clients MAY use
when presenting the relevant data to a user. Typically, this
would appear as the "background" color of events or tasks. The
value is a case-insensitive color name taken from the CSS3 set of
names, defined in Section 4.3 of [W3C.REC-css3-color-20110607].
Example: ``"turquoise"``, ``"#ffffff"``
>>> from icalendar import Calendar
>>> calendar = Calendar()
>>> calendar.color = "black"
>>> print(calendar.to_ical())
BEGIN:VCALENDAR
COLOR:black
END:VCALENDAR
"""
)
# These are read only singleton, so one instance is enough for the module
types_factory = TypesFactory()

Wyświetl plik

@ -0,0 +1,114 @@
"""This tests additional attributes from RFC 7986.
Some attributes are also available as X-... attributes.
They are also considered.
"""
import pytest
from icalendar import Calendar
from icalendar.prop import vText
@pytest.fixture()
def calendar() -> Calendar:
"""Empty calendar"""
return Calendar()
param_name = pytest.mark.parametrize("name", ["Company Vacation Days", "Calendar Name"])
param_prop = pytest.mark.parametrize("prop", ["NAME", "X-WR-CALNAME"])
@param_prop
@param_name
def test_get_calendar_name(prop, name, calendar):
"""Get the name of the calendar."""
calendar.add(prop, name)
assert calendar.calendar_name == name
@param_name
def test_set_calendar_name(name, calendar):
"""Setting the name overrides the old attributes."""
calendar.calendar_name = name
assert calendar.calendar_name == name
assert calendar["NAME"] == name
@param_name
@param_prop
def test_replace_name(name, prop, calendar):
"""Setting the name overrides the old attributes."""
calendar[prop] = "Other Name"
calendar.calendar_name = name
assert calendar.calendar_name == name
@param_name
@param_prop
def test_del_name(name, calendar, prop):
"""Delete the name."""
calendar.add(prop, name)
del calendar.calendar_name
assert calendar.calendar_name is None
def test_default_name(calendar):
"""We have no name by default."""
assert calendar.calendar_name is None
@param_name
def test_setting_the_name_deletes_the_non_standard_attribute(calendar, name):
"""The default_attr is deleted when setting the name."""
calendar["X-WR-CALNAME"] = name
assert "X-WR-CALNAME" in calendar
calendar.calendar_name = "other name"
assert "X-WR-CALNAME" not in calendar
@param_name
@pytest.mark.parametrize("order", [1, 2])
def test_multiple_names_use_the_one_without_a_language(calendar, name, order):
"""Add several names and use the one without a language param."""
if order == 1:
calendar.add("NAME", name)
calendar.add("NAME", vText("Kalendername", params={"LANGUAGE":"de"}))
if order == 2:
calendar.add("NAME", name)
assert calendar.calendar_name == name
@param_name
def test_name_is_preferred(calendar, name):
"""NAME is more important that X-WR-CALNAME"""
calendar.add("NAME", name)
calendar.add("X-WR-CALNAME", "asd")
assert calendar.calendar_name == name
# For description, we would use the same tests as name but we also use the
# same code, so it is alright.
param_color = pytest.mark.parametrize("desc", ["DESCRIPTION", "X-WR-CALDESC"])
@param_color
@param_name
def test_description(calendar, desc, name):
"""Get the value"""
calendar.add(desc, name)
assert calendar.calendar_description == name
# For color, we would use the same tests as name but we also use the
# same code, so it is alright.
param_color = pytest.mark.parametrize("color", ["COLOR", "X-APPLE-CALENDAR-COLOR"])
@param_color
@param_name
def test_color(calendar, color, name):
"""Get the value"""
calendar.add(color, name)
assert calendar.calendar_color == name