Implementing filter and sort in back
This commit is contained in:
@@ -6,7 +6,7 @@ from uuid import UUID
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from ..core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry
|
from ..core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry
|
||||||
from ..core.filter import Filter
|
from ..core.filter import Filter, FilterSchema
|
||||||
from ..entity.models import Entity
|
from ..entity.models import Entity
|
||||||
|
|
||||||
|
|
||||||
@@ -247,20 +247,17 @@ def replace_variables_in_value(variables, value: str):
|
|||||||
value = value.replace('%{}%'.format(v.key), v.value)
|
value = value.replace('%{}%'.format(v.key), v.value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
class ContractDraftFilters(Filter):
|
|
||||||
name__like: Optional[str] = None
|
|
||||||
|
|
||||||
order_by: Optional[list[str]] = None
|
class ContractDraftFilters(FilterSchema):
|
||||||
|
status: Optional[str] = None
|
||||||
|
|
||||||
class Constants(Filter.Constants):
|
class Constants(Filter.Constants):
|
||||||
model = ContractDraft
|
model = ContractDraft
|
||||||
search_model_fields = ["name"]
|
search_model_fields = ["label", "status"]
|
||||||
|
|
||||||
class ContractFilters(Filter):
|
class ContractFilters(FilterSchema):
|
||||||
name__like: Optional[str] = None
|
status: Optional[str] = None
|
||||||
|
|
||||||
order_by: Optional[list[str]] = None
|
|
||||||
|
|
||||||
class Constants(Filter.Constants):
|
class Constants(Filter.Constants):
|
||||||
model = Contract
|
model = Contract
|
||||||
search_model_fields = ["name"]
|
search_model_fields = ["label", "status"]
|
||||||
|
|||||||
@@ -46,10 +46,21 @@ class Filter(BaseFilterModel):
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def sort(self, query):
|
def sort(self):
|
||||||
if not self.ordering_values:
|
if not self.ordering_values:
|
||||||
return query
|
return None
|
||||||
return query.sort(*self.ordering_values)
|
|
||||||
|
sort = {}
|
||||||
|
for column in self.ordering_values:
|
||||||
|
direction = 1
|
||||||
|
if column[0] in ["+", "-"]:
|
||||||
|
if column[0] == "-":
|
||||||
|
direction = -1
|
||||||
|
column = column[1:]
|
||||||
|
|
||||||
|
sort[column] = direction
|
||||||
|
|
||||||
|
return sort
|
||||||
|
|
||||||
@field_validator("*", mode="before")
|
@field_validator("*", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -112,5 +123,13 @@ class Filter(BaseFilterModel):
|
|||||||
def filter(self, query):
|
def filter(self, query):
|
||||||
data = self._get_filter_conditions()
|
data = self._get_filter_conditions()
|
||||||
for filter_condition, filter_kwargs in data:
|
for filter_condition, filter_kwargs in data:
|
||||||
query = query.find(filter_condition, **filter_kwargs)
|
for field_name, value in filter_condition.items():
|
||||||
|
if field_name in query:
|
||||||
|
query[field_name] = query[field_name] | value
|
||||||
|
else:
|
||||||
|
query[field_name] = value
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
class FilterSchema(Filter):
|
||||||
|
label__ilike: Optional[str] = None
|
||||||
|
order_by: Optional[list[str]] = None
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from datetime import datetime, UTC
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from beanie import PydanticObjectId
|
from beanie import PydanticObjectId
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorCollection
|
||||||
from pydantic import BaseModel, Field, computed_field
|
from pydantic import BaseModel, Field, computed_field
|
||||||
|
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ class CrudDocument(BaseModel):
|
|||||||
return cls.__name__
|
return cls.__name__
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_collection(cls, db):
|
def _get_collection(cls, db) -> AsyncIOMotorCollection:
|
||||||
return db.get_collection(cls._collection_name())
|
return db.get_collection(cls._collection_name())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -42,10 +43,16 @@ class CrudDocument(BaseModel):
|
|||||||
return await cls.get(db, result.inserted_id)
|
return await cls.get(db, result.inserted_id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, db, filters):
|
def find(cls, db, filters):
|
||||||
query = filters.filter(cls._get_collection(db))
|
return {
|
||||||
query = filters.sort(query)
|
"collection": cls._get_collection(db),
|
||||||
return query
|
"query_filter": filters.filter({}),
|
||||||
|
"sort": filters.sort(),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls, db):
|
||||||
|
return cls._get_collection(db).find({})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get(cls, db, model_id):
|
async def get(cls, db, model_id):
|
||||||
|
|||||||
@@ -12,55 +12,6 @@ from .schemas import Writer, Reader
|
|||||||
from ..db import get_db_client
|
from ..db import get_db_client
|
||||||
|
|
||||||
|
|
||||||
def parse_sort(sort_by):
|
|
||||||
if not sort_by:
|
|
||||||
return []
|
|
||||||
|
|
||||||
fields = []
|
|
||||||
for field in sort_by.split(','):
|
|
||||||
direction, column = field.split('(')
|
|
||||||
fields.append((column[:-1], 1 if direction == 'asc' else -1))
|
|
||||||
|
|
||||||
return fields
|
|
||||||
|
|
||||||
|
|
||||||
def Or(filters):
|
|
||||||
return {'$or': filters}
|
|
||||||
|
|
||||||
|
|
||||||
def parse_query(query: str, model):
|
|
||||||
if query is None:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
and_array = []
|
|
||||||
for criterion in query.split(' AND '):
|
|
||||||
[column, operator, value] = criterion.split(' ', 2)
|
|
||||||
column = column.lower()
|
|
||||||
operand = None
|
|
||||||
if column == 'fulltext':
|
|
||||||
if not model.Settings.fulltext_search:
|
|
||||||
continue
|
|
||||||
|
|
||||||
or_array = []
|
|
||||||
for field in model.Settings.fulltext_search:
|
|
||||||
words_and_array = []
|
|
||||||
for word in value.split(' '):
|
|
||||||
words_and_array.append(RegEx(field, word, 'i'))
|
|
||||||
or_array.append(And(*words_and_array) if len(words_and_array) > 1 else words_and_array[0])
|
|
||||||
operand = Or(or_array) if len(or_array) > 1 else or_array[0]
|
|
||||||
|
|
||||||
elif operator == 'eq':
|
|
||||||
operand = Eq(column, value)
|
|
||||||
elif operator == 'in':
|
|
||||||
operand = In(column, value.split(','))
|
|
||||||
|
|
||||||
if operand:
|
|
||||||
and_array.append(operand)
|
|
||||||
|
|
||||||
if and_array:
|
|
||||||
return And(*and_array) if len(and_array) > 1 else and_array[0]
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
#instance: str="westside", firm: str="cht",
|
#instance: str="westside", firm: str="cht",
|
||||||
def get_tenant_db_cursor(db_client=Depends(get_db_client)):
|
def get_tenant_db_cursor(db_client=Depends(get_db_client)):
|
||||||
instance = "westside"
|
instance = "westside"
|
||||||
@@ -80,7 +31,7 @@ def get_crud_router(model: CrudDocument, model_create: Writer, model_read: Reade
|
|||||||
|
|
||||||
@router.get("/", response_model=Page[model_read], response_description=f"{model_name} records retrieved")
|
@router.get("/", response_model=Page[model_read], response_description=f"{model_name} records retrieved")
|
||||||
async def read_list(filters: model_filter=FilterDepends(model_filter), db=Depends(get_logged_tenant_db_cursor)) -> Page[model_read]:
|
async def read_list(filters: model_filter=FilterDepends(model_filter), db=Depends(get_logged_tenant_db_cursor)) -> Page[model_read]:
|
||||||
return await paginate(model.list(db, filters))
|
return await paginate(**model.find(db, filters))
|
||||||
|
|
||||||
@router.post("/", response_description=f"{model_name} added to the database")
|
@router.post("/", response_description=f"{model_name} added to the database")
|
||||||
async def create(schema: model_create, db=Depends(get_logged_tenant_db_cursor)) -> model_read:
|
async def create(schema: model_create, db=Depends(get_logged_tenant_db_cursor)) -> model_read:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from pydantic import Field, BaseModel
|
|||||||
from beanie import Indexed
|
from beanie import Indexed
|
||||||
|
|
||||||
from ..core.models import CrudDocument
|
from ..core.models import CrudDocument
|
||||||
from ..core.filter import Filter
|
from ..core.filter import Filter, FilterSchema
|
||||||
|
|
||||||
|
|
||||||
class EntityType(BaseModel):
|
class EntityType(BaseModel):
|
||||||
@@ -95,11 +95,7 @@ class Entity(CrudDocument):
|
|||||||
title = 'Client'
|
title = 'Client'
|
||||||
|
|
||||||
|
|
||||||
class EntityFilters(Filter):
|
class EntityFilters(FilterSchema):
|
||||||
name__like: Optional[str] = None
|
|
||||||
|
|
||||||
order_by: Optional[list[str]] = None
|
|
||||||
|
|
||||||
class Constants(Filter.Constants):
|
class Constants(Filter.Constants):
|
||||||
model = Entity
|
model = Entity
|
||||||
search_model_fields = ["name"]
|
search_model_fields = ["label"]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from html import unescape
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from ..core.models import CrudDocument, RichtextMultiline, RichtextSingleline, DictionaryEntry
|
from ..core.models import CrudDocument, RichtextMultiline, RichtextSingleline, DictionaryEntry
|
||||||
from ..core.filter import Filter
|
from ..core.filter import Filter, FilterSchema
|
||||||
|
|
||||||
|
|
||||||
class PartyTemplate(BaseModel):
|
class PartyTemplate(BaseModel):
|
||||||
@@ -105,21 +105,13 @@ class ContractTemplate(CrudDocument):
|
|||||||
title = 'Template de contrat'
|
title = 'Template de contrat'
|
||||||
|
|
||||||
|
|
||||||
class ContractTemplateFilters(Filter):
|
class ContractTemplateFilters(FilterSchema):
|
||||||
name__like: Optional[str] = None
|
|
||||||
|
|
||||||
order_by: Optional[list[str]] = None
|
|
||||||
|
|
||||||
class Constants(Filter.Constants):
|
class Constants(Filter.Constants):
|
||||||
model = ContractTemplate
|
model = ContractTemplate
|
||||||
search_model_fields = ["name"]
|
search_model_fields = ["label"]
|
||||||
|
|
||||||
|
|
||||||
class ProvisionTemplateFilters(Filter):
|
class ProvisionTemplateFilters(FilterSchema):
|
||||||
name__like: Optional[str] = None
|
|
||||||
|
|
||||||
order_by: Optional[list[str]] = None
|
|
||||||
|
|
||||||
class Constants(Filter.Constants):
|
class Constants(Filter.Constants):
|
||||||
model = ProvisionTemplate
|
model = ProvisionTemplate
|
||||||
search_model_fields = ["name"]
|
search_model_fields = ["label"]
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export class ListComponent implements OnInit {
|
|||||||
private _search() {
|
private _search() {
|
||||||
this._loading$.next(true);
|
this._loading$.next(true);
|
||||||
let sortBy = new SortBy(this.sortColumn, this.sortDirection)
|
let sortBy = new SortBy(this.sortColumn, this.sortDirection)
|
||||||
let filters = this.searchTerm ? [new Filters('fulltext', 'eq', this.searchTerm)] : [];
|
let filters = this.searchTerm ? [new Filters('label', 'ilike', this.searchTerm)] : [];
|
||||||
for (let f in this.searchFilters) {
|
for (let f in this.searchFilters) {
|
||||||
if (Array.isArray(this.searchFilters[f])) {
|
if (Array.isArray(this.searchFilters[f])) {
|
||||||
filters.push(new Filters(f, 'in', this.searchFilters[f]))
|
filters.push(new Filters(f, 'in', this.searchFilters[f]))
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
|||||||
1,
|
1,
|
||||||
10,
|
10,
|
||||||
[],
|
[],
|
||||||
[new Filters('fulltext', 'eq', term)]
|
[new Filters('label', 'ilike', term)]
|
||||||
).pipe(
|
).pipe(
|
||||||
map((result: any) => result["items"]),
|
map((result: any) => result["items"]),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user