Add python proxy/filter models
This commit is contained in:
parent
1a69ae684e
commit
eee198b238
102
src/backend/models/filters.py
Normal file
102
src/backend/models/filters.py
Normal 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
|
|
@ -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"] = {}
|
||||||
|
|
44
src/backend/models/proxy.py
Normal file
44
src/backend/models/proxy.py
Normal 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]
|
17
src/backend/models/special_models.py
Normal file
17
src/backend/models/special_models.py
Normal 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
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user