Add python proxy/filter models

This commit is contained in:
miruka 2020-05-06 00:26:52 -04:00
parent 1a69ae684e
commit eee198b238
4 changed files with 168 additions and 0 deletions

View File

@ -0,0 +1,102 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
from typing import TYPE_CHECKING, Collection, Dict, Optional, Tuple
from . import SyncId
from .model import Model
from .proxy import ModelProxy
if TYPE_CHECKING:
from .model_item import ModelItem
class ModelFilter(ModelProxy):
def __init__(self, sync_id: SyncId) -> None:
self.filtered_out: Dict[Tuple[Optional[SyncId], str], "ModelItem"] = {}
super().__init__(sync_id)
def accept_item(self, item: "ModelItem") -> bool:
return True
def source_item_set(self, source: Model, key, value: "ModelItem") -> None:
if self.accept_source(source):
dct = self if self.accept_item(value) else self.filtered_out
dct[source.sync_id, key] = value
def source_item_deleted(self, source: Model, key) -> None:
if self.accept_source(source):
try:
del self[source.sync_id, key]
except KeyError:
del self.filtered_out[source.sync_id, key]
def source_cleared(self, source: Model) -> None:
if self.accept_source(source):
for source_sync_id, key in self.copy():
if source_sync_id == source.sync_id:
try:
del self[source.sync_id, key]
except KeyError:
del self.filtered_out[source.sync_id, key]
def refilter(self) -> None:
take_out = []
bring_back = []
for key, item in self.items():
if not self.accept_item(item):
take_out.append(key)
for key, item in self.filtered_out.items():
if self.accept_item(item):
bring_back.append(key)
for key in take_out:
self.filtered_out[key] = self.pop(key)
for key in bring_back:
self[key] = self.filtered_out.pop(key)
class FieldSubstringFilter(ModelFilter):
def __init__(self, fields: Collection[str], *args, **kwargs) -> None:
self.fields: Collection[str] = fields
self._filter: str = ""
super().__init__(*args, **kwargs)
@property
def filter(self) -> str:
return self._filter
@filter.setter
def filter(self, value: str) -> None:
self._filter = value
self.refilter()
def accept_item(self, item: "ModelItem") -> bool:
if not self.filter:
return True
text = " ".join((getattr(item, f) for f in self.fields))
filt = self.filter
filt_lower = filt.lower()
if filt_lower == filt:
# Consider case only if filter isn't all lowercase (smart case)
filt = filt_lower
text = text.lower()
for word in filt.split():
if word and word not in text:
return False
return True

View File

@ -13,6 +13,7 @@ from . import SyncId
if TYPE_CHECKING: if TYPE_CHECKING:
from .model_item import ModelItem from .model_item import ModelItem
from .proxy import ModelProxy # noqa
class Model(MutableMapping): class Model(MutableMapping):
@ -29,6 +30,10 @@ class Model(MutableMapping):
Items in the model are kept sorted using the `ModelItem` subclass `__lt__`. Items in the model are kept sorted using the `ModelItem` subclass `__lt__`.
""" """
instances: Dict[SyncId, "Model"] = {}
proxies: Dict[SyncId, "ModelProxy"] = {}
def __init__(self, sync_id: Optional[SyncId]) -> None: def __init__(self, sync_id: Optional[SyncId]) -> None:
self.sync_id: Optional[SyncId] = sync_id self.sync_id: Optional[SyncId] = sync_id
self._data: Dict[Any, "ModelItem"] = {} self._data: Dict[Any, "ModelItem"] = {}

View File

@ -0,0 +1,44 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
from typing import TYPE_CHECKING
from . import SyncId
from .model import Model
if TYPE_CHECKING:
from .model_item import ModelItem
class ModelProxy(Model):
def __init__(self, sync_id: SyncId) -> None:
super().__init__(sync_id)
Model.proxies[sync_id] = self
for sync_id, model in Model.instances.items():
if sync_id != self.sync_id and self.accept_source(model):
for key, item in model.items():
# if isinstance(model, ModelProxy):
# key = key[1]
self.source_item_set(model, key, item)
def accept_source(self, source: Model) -> bool:
return True
def source_item_set(self, source: Model, key, value: "ModelItem") -> None:
if self.accept_source(source):
self[source.sync_id, key] = value
def source_item_deleted(self, source: Model, key) -> None:
if self.accept_source(source):
del self[source.sync_id, key]
def source_cleared(self, source: Model) -> None:
if self.accept_source(source):
for source_sync_id, key in self.copy():
if source_sync_id == source.sync_id:
del self[source_sync_id, key]

View File

@ -0,0 +1,17 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
from .filters import FieldSubstringFilter
from .model import Model
class AllRooms(FieldSubstringFilter):
def __init__(self) -> None:
super().__init__(sync_id="all_rooms", fields=("display_name",))
def accept_source(self, source: Model) -> bool:
return (
isinstance(source.sync_id, tuple) and
len(source.sync_id) == 2 and
source.sync_id[1] == "rooms" # type: ignore
)