kopia lustrzana https://github.com/collective/icalendar
Add categories property
rodzic
33f554fb2f
commit
cca55fc8c2
|
|
@ -17,6 +17,7 @@ New features:
|
|||
|
||||
- Add attributes to the calendar for properties ``NAME``, ``DESCRIPTION``, and ``COLOR``. See `Issue 655 <https://github.com/collective/icalendar/issues/655>`_.
|
||||
- Add ``sequence`` attribute to ``Event``, ``Todo``, and ``Journal`` components. See `Issue 802 <https://github.com/collective/icalendar/issues/802>`_.
|
||||
- Add ``categories`` attribute to ``Calendar``, ``Event``, ``Todo``, and ``Journal`` components. See `Issue 655 <https://github.com/collective/icalendar/issues/655>`_.
|
||||
- Add compatibility to :rfc:`6868`. See `Issue 652 <https://github.com/collective/icalendar/issues/652>`_.
|
||||
|
||||
Bug fixes:
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
import itertools
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from icalendar.error import InvalidCalendar
|
||||
from icalendar.prop import vDDDTypes, vText
|
||||
from icalendar.prop import vCategory, vDDDTypes, vText
|
||||
from icalendar.timezone import tzp
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -179,9 +180,79 @@ Examples:
|
|||
"""
|
||||
)
|
||||
|
||||
def _get_categories(component: Component) -> list[str]:
|
||||
"""Get all the categories."""
|
||||
categories : Optional[vCategory|list[vCategory]] = component.get("CATEGORIES")
|
||||
if isinstance(categories, list):
|
||||
_set_categories(component, list(itertools.chain.from_iterable(cat.cats for cat in categories)))
|
||||
return _get_categories(component)
|
||||
if categories is None:
|
||||
categories = vCategory([])
|
||||
component.add("CATEGORIES", categories)
|
||||
return categories.cats
|
||||
|
||||
def _set_categories(component: Component, cats: list[str]) -> None:
|
||||
"""Set the categories."""
|
||||
component["CATEGORIES"] = categories = vCategory(cats)
|
||||
cats.clear()
|
||||
cats.extend(categories.cats)
|
||||
categories.cats = cats
|
||||
|
||||
|
||||
def _del_categories(component: Component) -> None:
|
||||
"""Delete the categories."""
|
||||
component.pop("CATEGORIES", None)
|
||||
|
||||
|
||||
categories_property = property(
|
||||
_get_categories,
|
||||
_set_categories,
|
||||
_del_categories,
|
||||
"""This property defines the categories for a component.
|
||||
|
||||
Property Parameters:
|
||||
|
||||
IANA, non-standard, and language property parameters can be specified on this
|
||||
property.
|
||||
|
||||
Conformance:
|
||||
|
||||
The property can be specified within "VEVENT", "VTODO", or "VJOURNAL" calendar
|
||||
components.
|
||||
Since :rfc:`7986` it can also be defined on a "VCALENDAR" component.
|
||||
|
||||
Description:
|
||||
|
||||
This property is used to specify categories or subtypes
|
||||
of the calendar component. The categories are useful in searching
|
||||
for a calendar component of a particular type and category.
|
||||
Within the "VEVENT", "VTODO", or "VJOURNAL" calendar components,
|
||||
more than one category can be specified as a COMMA-separated list
|
||||
of categories.
|
||||
|
||||
Example:
|
||||
|
||||
>>> from icalendar import Event
|
||||
>>> event = Event()
|
||||
>>> event.categories = ["Work", "Meeting"]
|
||||
>>> print(event.to_ical())
|
||||
BEGIN:VEVENT
|
||||
CATEGORIES:Work,Meeting
|
||||
END:VEVENT
|
||||
>>> event.categories.append("Lecture")
|
||||
>>> event.categories == ["Work", "Meeting", "Lecture"]
|
||||
True
|
||||
|
||||
.. note::
|
||||
|
||||
At present, we do not take the LANGUAGE parameter into account.
|
||||
"""
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"single_utc_property",
|
||||
"multi_language_text_property",
|
||||
"single_int_property",
|
||||
"sequence_property"
|
||||
"sequence_property",
|
||||
"categories_property",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import dateutil.rrule
|
|||
import dateutil.tz
|
||||
|
||||
from icalendar.attr import (
|
||||
categories_property,
|
||||
multi_language_text_property,
|
||||
sequence_property,
|
||||
single_int_property,
|
||||
|
|
@ -894,6 +895,7 @@ class Event(Component):
|
|||
X_MOZ_SNOOZE_TIME = _X_MOZ_SNOOZE_TIME
|
||||
X_MOZ_LASTACK = _X_MOZ_LASTACK
|
||||
sequence = sequence_property
|
||||
categories = categories_property
|
||||
|
||||
|
||||
class Todo(Component):
|
||||
|
|
@ -1083,6 +1085,7 @@ class Todo(Component):
|
|||
return Alarms(self)
|
||||
|
||||
sequence = sequence_property
|
||||
categories = categories_property
|
||||
|
||||
|
||||
class Journal(Component):
|
||||
|
|
@ -1168,6 +1171,7 @@ class Journal(Component):
|
|||
return timedelta(0)
|
||||
|
||||
sequence = sequence_property
|
||||
categories = categories_property
|
||||
|
||||
|
||||
class FreeBusy(Component):
|
||||
|
|
@ -2101,6 +2105,7 @@ class Calendar(Component):
|
|||
END:VCALENDAR
|
||||
"""
|
||||
)
|
||||
categories = categories_property
|
||||
|
||||
# These are read only singleton, so one instance is enough for the module
|
||||
types_factory = TypesFactory()
|
||||
|
|
|
|||
|
|
@ -430,17 +430,20 @@ class vDDDLists:
|
|||
class vCategory:
|
||||
params: Parameters
|
||||
|
||||
def __init__(self, c_list, params={}):
|
||||
def __init__(self, c_list:list[str] | str, params={}):
|
||||
if not hasattr(c_list, "__iter__") or isinstance(c_list, str):
|
||||
c_list = [c_list]
|
||||
self.cats = [vText(c) for c in c_list]
|
||||
self.cats : list[vText|str] = [vText(c) for c in c_list]
|
||||
self.params = Parameters(params)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(vCategory.from_ical(self.to_ical()))
|
||||
|
||||
def to_ical(self):
|
||||
return b",".join([c.to_ical() for c in self.cats])
|
||||
return b",".join([
|
||||
c.to_ical() if hasattr(c, "to_ical") else vText(c).to_ical()
|
||||
for c in self.cats
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def from_ical(ical):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"""This tests the compatibility with RFC 7529.
|
||||
|
||||
See
|
||||
- https://github.com/collective/icalendar/issues/653
|
||||
- https://github.com/collective/icalendar/issues/655
|
||||
- https://www.rfc-editor.org/rfc/rfc7529.html
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
"""This tests the compatibility with RFC 7529.
|
||||
|
||||
CATEGORIES property
|
||||
|
||||
See
|
||||
- https://github.com/collective/icalendar/issues/655
|
||||
- https://www.rfc-editor.org/rfc/rfc7529.html
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
|
||||
from icalendar import Calendar, Event, Journal, Todo
|
||||
|
||||
CTJE = Union[Calendar, Event, Journal, Todo]
|
||||
|
||||
@pytest.fixture(params=[Event, Calendar, Todo, Journal])
|
||||
def component(request):
|
||||
"""An empty component with possible categories."""
|
||||
return request.param()
|
||||
|
||||
|
||||
def test_no_categories_at_creation(component: CTJE):
|
||||
"""An empty component has no categories."""
|
||||
assert "CATEGORIES" not in component
|
||||
assert component.categories == []
|
||||
|
||||
|
||||
def test_add_one_category(component: CTJE):
|
||||
"""Add one category."""
|
||||
component.add("categories", "Lecture")
|
||||
assert component.categories == ["Lecture"]
|
||||
|
||||
def test_add_multiple_categories(component: CTJE):
|
||||
"""Add categories."""
|
||||
component.add("categories", ["Lecture", "Workshop"])
|
||||
assert component.categories == ["Lecture", "Workshop"]
|
||||
|
||||
def test_set_categories(component: CTJE):
|
||||
"""Set categories."""
|
||||
component.categories = ["Lecture", "Workshop"]
|
||||
assert component.categories == ["Lecture", "Workshop"]
|
||||
|
||||
|
||||
def test_modify_list(component: CTJE):
|
||||
"""Modify the list and it still works."""
|
||||
component.categories = categories = ["cat1"]
|
||||
categories.append("cat2")
|
||||
assert component.categories == ["cat1", "cat2"]
|
||||
|
||||
|
||||
def test_delete_categories(component: CTJE):
|
||||
"""Delete categories."""
|
||||
component.categories = ["Lecture", "Workshop"]
|
||||
del component.categories
|
||||
assert "CATEGORIES" not in component
|
||||
assert component.categories == []
|
||||
|
||||
|
||||
def test_deal_with_several_categories(component: CTJE):
|
||||
"""If we have categories several times, we should all use them."""
|
||||
component.add("categories", ["c1", "c2"])
|
||||
component.add("categories", ["c3", "c4"])
|
||||
assert component.categories == ["c1", "c2", "c3", "c4"]
|
||||
component.categories.append("c5")
|
||||
assert component.categories == ["c1", "c2", "c3", "c4", "c5"]
|
||||
component.categories.remove("c2")
|
||||
assert component.categories == ["c1", "c3", "c4", "c5"]
|
||||
assert "c1,c3,c4,c5" in component.to_ical().decode()
|
||||
Ładowanie…
Reference in New Issue