"""Selecting components by name.""" from __future__ import annotations import contextlib from collections import defaultdict from typing import TYPE_CHECKING, Sequence from recurring_ical_events.adapters.event import EventAdapter from recurring_ical_events.adapters.journal import JournalAdapter from recurring_ical_events.adapters.todo import TodoAdapter from recurring_ical_events.occurrence import Occurrence from recurring_ical_events.selection.alarm import Alarms from recurring_ical_events.selection.base import SelectComponents from recurring_ical_events.series import Series from recurring_ical_events.util import cached_property if TYPE_CHECKING: from icalendar.cal import Component from recurring_ical_events.adapters.component import ComponentAdapter class ComponentsWithName(SelectComponents): """This is a component collecttion strategy. Components can be collected in different ways. This class allows extension of the functionality by - subclassing to filter the resulting components - composition to combine collection behavior (see AllKnownComponents) """ component_adapters: list[type[ComponentAdapter] | SelectComponents] = [ EventAdapter, TodoAdapter, JournalAdapter, Alarms(), ] @cached_property def _component_adapters(self) -> dict[str : type[ComponentAdapter]]: """A mapping of component adapters.""" return { adapter.component_name(): adapter for adapter in self.component_adapters } def __init__( self, name: str, adapter: type[ComponentAdapter] | None = None, series: type[Series] = Series, occurrence: type[Occurrence] = Occurrence, ) -> None: """Create a new way of collecting components. name - the name of the component to collect ("VEVENT", "VTODO", "VJOURNAL") adapter - the adapter to use for these components with that name series - the series class that hold a series of components occurrence - the occurrence class that creates the resulting components """ if adapter is None: if name not in self._component_adapters: raise ValueError( f'"{name}" is an unknown name for a ' "recurring component. " f"I only know these: {', '.join(self._component_adapters)}." ) adapter = self._component_adapters[name] if occurrence is not Occurrence: _occurrence = occurrence class series(series): # noqa: N801 occurrence = _occurrence self._name = name self._series = series self._adapter = adapter def collect_series_from( self, source: Component, suppress_errors: tuple[Exception] ) -> Sequence[Series]: """Collect all components from the source component. suppress_errors - a list of errors that should be suppressed. A Series of events with such an error is removed from all results. """ if isinstance(self._adapter, SelectComponents): return self._adapter.collect_series_from(source, suppress_errors) components: dict[str, list[Component]] = defaultdict(list) # UID -> components for component in source.walk(self._name): adapter = self._adapter(component) components[adapter.uid].append(adapter) result = [] for components in components.values(): with contextlib.suppress(suppress_errors): result.append(self._series(components)) return result __all__ = ["ComponentsWithName"]