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:
|
||||
from .model_item import ModelItem
|
||||
from .proxy import ModelProxy # noqa
|
||||
|
||||
|
||||
class Model(MutableMapping):
|
||||
|
@ -29,6 +30,10 @@ class Model(MutableMapping):
|
|||
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:
|
||||
self.sync_id: Optional[SyncId] = sync_id
|
||||
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