fix: 포트 충돌 회피 — note_bridge 8098, intent_service 8099
Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
"""Select components for calculation."""
|
||||
|
||||
from .alarm import Alarms
|
||||
from .all import AllKnownComponents
|
||||
from .base import SelectComponents
|
||||
from .name import ComponentsWithName
|
||||
|
||||
__all__ = [
|
||||
"Alarms",
|
||||
"AllKnownComponents",
|
||||
"ComponentsWithName",
|
||||
"SelectComponents",
|
||||
]
|
||||
@@ -0,0 +1,93 @@
|
||||
"""Selection for alarms."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Sequence
|
||||
|
||||
from recurring_ical_events.adapters.event import EventAdapter
|
||||
from recurring_ical_events.adapters.todo import TodoAdapter
|
||||
from recurring_ical_events.selection.base import SelectComponents
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from icalendar.cal import Component
|
||||
|
||||
from recurring_ical_events.adapters.component import ComponentAdapter
|
||||
from recurring_ical_events.series import Series
|
||||
|
||||
|
||||
class Alarms(SelectComponents):
|
||||
"""Select alarms and find their times.
|
||||
|
||||
By default, alarms from TODOs and events are collected.
|
||||
You can use this to change which alarms are collected:
|
||||
|
||||
Alarms((EventAdapter,))
|
||||
Alarms((TodoAdapter,))
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parents: tuple[type[ComponentAdapter] | SelectComponents] = (
|
||||
EventAdapter,
|
||||
TodoAdapter,
|
||||
),
|
||||
):
|
||||
self.parents = parents
|
||||
|
||||
@staticmethod
|
||||
def component_name():
|
||||
"""The name of the component we calculate."""
|
||||
return "VALARM"
|
||||
|
||||
def collect_parent_series_from(
|
||||
self, source: Component, suppress_errors: tuple[Exception]
|
||||
) -> Sequence[Series]:
|
||||
"""Collect the parent components of alarms."""
|
||||
return [
|
||||
s
|
||||
for parent in self.parents
|
||||
for s in parent.collect_series_from(source, suppress_errors)
|
||||
]
|
||||
|
||||
def collect_series_from(
|
||||
self, source: Component, suppress_errors: tuple[Exception]
|
||||
) -> Sequence[Series]:
|
||||
"""Collect all TODOs and Alarms from VEVENTs and VTODOs.
|
||||
|
||||
suppress_errors - a list of errors that should be suppressed.
|
||||
A Series of events with such an error is removed from all results.
|
||||
"""
|
||||
from recurring_ical_events.series.alarm import (
|
||||
AbsoluteAlarmSeries,
|
||||
AlarmSeriesRelativeToEnd,
|
||||
AlarmSeriesRelativeToStart,
|
||||
)
|
||||
|
||||
absolute_alarms = AbsoluteAlarmSeries()
|
||||
result = []
|
||||
# alarms might be copied several times. We only compute them once.
|
||||
for series in self.collect_parent_series_from(source, suppress_errors):
|
||||
used_alarms = []
|
||||
for component in series.components:
|
||||
for alarm in component.alarms:
|
||||
with contextlib.suppress(suppress_errors):
|
||||
trigger = alarm.TRIGGER
|
||||
if trigger is None or alarm in used_alarms:
|
||||
continue
|
||||
if isinstance(trigger, datetime.datetime):
|
||||
absolute_alarms.add(alarm, component)
|
||||
used_alarms.append(alarm)
|
||||
elif alarm.TRIGGER_RELATED == "START":
|
||||
result.append(AlarmSeriesRelativeToStart(alarm, series))
|
||||
used_alarms.append(alarm)
|
||||
elif alarm.TRIGGER_RELATED == "END":
|
||||
result.append(AlarmSeriesRelativeToEnd(alarm, series))
|
||||
used_alarms.append(alarm)
|
||||
if not absolute_alarms.is_empty():
|
||||
result.append(absolute_alarms)
|
||||
return result
|
||||
|
||||
|
||||
__all__ = ["Alarms"]
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Selection of all components with the correct adapters."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Sequence
|
||||
|
||||
from recurring_ical_events.occurrence import Occurrence
|
||||
from recurring_ical_events.selection.base import SelectComponents
|
||||
from recurring_ical_events.selection.name import ComponentsWithName
|
||||
from recurring_ical_events.series import Series
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from icalendar.cal import Component
|
||||
|
||||
from recurring_ical_events.adapters.component import ComponentAdapter
|
||||
|
||||
|
||||
class AllKnownComponents(SelectComponents):
|
||||
"""Group all known components into series."""
|
||||
|
||||
@property
|
||||
def _component_adapters(self) -> Sequence[ComponentAdapter]:
|
||||
"""Return all known component adapters."""
|
||||
return ComponentsWithName.component_adapters
|
||||
|
||||
@property
|
||||
def names(self) -> list[str]:
|
||||
"""Return the names of the components to collect."""
|
||||
result = [adapter.component_name() for adapter in self._component_adapters]
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
series: type[Series] = Series,
|
||||
occurrence: type[Occurrence] = Occurrence,
|
||||
collector: type[ComponentsWithName] = ComponentsWithName,
|
||||
) -> None:
|
||||
"""Collect all known components and overide the series and occurrence.
|
||||
|
||||
series - the Series class to override that is queried for Occurrences
|
||||
occurrence - the occurrence class that creates the resulting components
|
||||
collector - if you want to override the SelectComponentsByName class
|
||||
"""
|
||||
self._series = series
|
||||
self._occurrence = occurrence
|
||||
self._collector = collector
|
||||
|
||||
def collect_series_from(
|
||||
self, source: Component, suppress_errors: tuple[Exception]
|
||||
) -> Sequence[Series]:
|
||||
"""Collect the components from the source groups into a series."""
|
||||
result = []
|
||||
for name in self.names:
|
||||
collector = self._collector(
|
||||
name, series=self._series, occurrence=self._occurrence
|
||||
)
|
||||
result.extend(collector.collect_series_from(source, suppress_errors))
|
||||
return result
|
||||
|
||||
|
||||
__all__ = ["AllKnownComponents"]
|
||||
@@ -0,0 +1,33 @@
|
||||
"""Base interface for selection of components."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Sequence
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from icalendar.cal import Component
|
||||
|
||||
from recurring_ical_events.series import Series
|
||||
|
||||
|
||||
class SelectComponents(ABC):
|
||||
"""Abstract class to select components from a calendar."""
|
||||
|
||||
@staticmethod
|
||||
def component_name():
|
||||
"""The name of the component if there is only one."""
|
||||
raise NotImplementedError("This should be implemented in subclasses.")
|
||||
|
||||
@abstractmethod
|
||||
def collect_series_from(
|
||||
self, source: Component, suppress_errors: tuple[Exception]
|
||||
) -> Sequence[Series]:
|
||||
"""Collect all components from the source grouped together into a series.
|
||||
|
||||
suppress_errors - a list of errors that should be suppressed.
|
||||
A Series of events with such an error is removed from all results.
|
||||
"""
|
||||
|
||||
|
||||
__all__ = ["SelectComponents"]
|
||||
@@ -0,0 +1,100 @@
|
||||
"""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"]
|
||||
Reference in New Issue
Block a user