Skip to content

Commit

Permalink
feat: TableFilters
Browse files Browse the repository at this point in the history
  • Loading branch information
smotornyuk committed Jan 27, 2024
1 parent 48a1b24 commit d7f1ff2
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 6 deletions.
8 changes: 6 additions & 2 deletions ckanext/collection/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ def get_collection_factories(self) -> dict[str, CollectionFactory]:
n,
p,
db_connection_settings={"engine": model.meta.engine},
columns_settings={"table": "activity"},
data_settings={"table": "activity"},
columns_settings={
"table": "package",
"filterable": {"type", "state", "private"},
},
data_settings={"table": "package", "use_naive_filters": True},
filters_settings={"table": "package"},
serializer_factory=HtmxTableSerializer,
),
}
Expand Down
4 changes: 2 additions & 2 deletions ckanext/collection/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,14 @@ class Filter(TypedDict, Generic[TFilterOptions]):
options: TFilterOptions


class _SelectOptions(TypedDict):
class SelectOption(TypedDict):
text: str
value: str


class SelectFilterOptions(TypedDict):
label: str
options: Sequence[_SelectOptions]
options: Sequence[SelectOption]


class InputFilterOptions(TypedDict):
Expand Down
3 changes: 2 additions & 1 deletion ckanext/collection/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
UnionModelData,
)
from .db_connection import DbConnection, UrlDbConnection
from .filters import Filters
from .filters import Filters, TableFilters
from .pager import ClassicPager, Pager
from .serialize import (
ChartJsSerializer,
Expand Down Expand Up @@ -55,6 +55,7 @@
"CsvSerializer",
"Data",
"Filters",
"TableFilters",
"HtmlSerializer",
"HtmxTableSerializer",
"JsonSerializer",
Expand Down
2 changes: 2 additions & 0 deletions ckanext/collection/utils/collection/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ckanext.collection.utils.columns import TableColunns
from ckanext.collection.utils.data import TableData
from ckanext.collection.utils.db_connection import DbConnection
from ckanext.collection.utils.filters import TableFilters

from .base import Collection

Expand All @@ -17,6 +18,7 @@ class DbCollection(Collection[types.TData], types.BaseDbCollection[types.TData])
DbConnectionFactory: type[DbConnection[Self]] = DbConnection
DataFactory = TableData
ColumnsFactory = TableColunns
FiltersFactory = TableFilters

def make_db_connection(self, **kwargs: Any) -> DbConnection[Self]:
"""Return connection."""
Expand Down
4 changes: 4 additions & 0 deletions ckanext/collection/utils/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ def get_secondary_order(self, name: str) -> str:

class TableColunns(Columns[types.TDbCollection]):
table: str = shared.configurable_attribute()
filterable: set[str] = shared.configurable_attribute(
default_factory=lambda self: set(),
)

def apply_names(self):
self.names = [
c["name"]
for c in self.attached.db_connection.inspector.get_columns(self.table)
]

super().apply_names()
5 changes: 4 additions & 1 deletion ckanext/collection/utils/data/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ def _execute(self, stmt: GenerativeSelect):
return self.attached.db_connection.engine.execute(stmt)

def get_base_statement(self):
return sa.select(sa.text("*")).select_from(sa.table(self.table))
columns = self.attached.db_connection.inspector.get_columns(self.table)
return sa.select(*[sa.column(c["name"]) for c in columns]).select_from(
sa.table(self.table),
)
1 change: 1 addition & 0 deletions ckanext/collection/utils/data/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def count_statement(self, stmt: TStatement) -> int:

def statement_with_filters(self, stmt: TStatement) -> TStatement:
"""Add normal filter to statement."""

if not isinstance(stmt, Select):
return stmt

Expand Down
59 changes: 59 additions & 0 deletions ckanext/collection/utils/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from typing import Any, Sequence

import sqlalchemy as sa

from ckanext.collection import shared, types


Expand Down Expand Up @@ -33,3 +35,60 @@ def __init__(self, obj: types.TDataCollection, /, **kwargs: Any):
super().__init__(obj, **kwargs)
self.filters = self.static_filters or self.make_filters()
self.actions = self.static_actions or self.make_actions()


class TableFilters(Filters[types.TDbCollection]):
table: str = shared.configurable_attribute()

def make_filters(self) -> Sequence[types.Filter[Any]]:
return [
filter
for c in self.attached.db_connection.inspector.get_columns(self.table)
if (filter := self._filter_for(c))
]

def _filter_for(self, column: dict[str, Any]) -> types.Filter[Any] | None:
if column["name"] in self.attached.columns.filterable:
if isinstance(column["type"], sa.Boolean):
return self._boolean_filter(column)

return self._distinct_filter(column)

return None

def _filter_label(self, column: dict[str, Any]) -> str:
return self.attached.columns.labels.get(column["name"], column["name"])

def _boolean_filter(self, column: dict[str, Any]) -> types.Filter[Any]:
options = [
types.SelectOption(text="All", value=""),
types.SelectOption(text="Yes", value="t"),
types.SelectOption(text="No", value="f"),
]
return types.SelectFilter(
name=column["name"],
type="select",
options=types.SelectFilterOptions(
label=self._filter_label(column),
options=options,
),
)

def _distinct_filter(self, column: dict[str, Any]) -> types.Filter[Any]:
with self.attached.db_connection.engine.connect() as conn:
values = conn.scalars(
sa.select(sa.distinct(sa.column(column["name"]))).select_from(
sa.table(self.table),
),
)
options = [types.SelectOption(text="All", value="")] + [
types.SelectOption(text=v, value=v) for v in values
]
return types.SelectFilter(
name=column["name"],
type="select",
options=types.SelectFilterOptions(
label=self._filter_label(column),
options=options,
),
)

0 comments on commit d7f1ff2

Please sign in to comment.