Initial commit

This commit is contained in:
shahzeydyfah
2024-02-15 00:17:51 +03:30
commit 4b0396466d
111 changed files with 6296 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
import abc
__all__ = "AbstractEngine"
class AbstractEngineClass(abc.ABC):
def __init__(self, table_engine):
self.model = table_engine.model
self.query_class = table_engine.query_class
self.input = table_engine.input
self.filter = table_engine.input.f
self.default_sort_by = table_engine.default_sort_by
self.custom_entities = []
self.joins = table_engine.joins
self.options = table_engine.options
self.search_cols = table_engine.search_cols
self.soft_deleted = table_engine.soft_deleted
@abc.abstractmethod
def exe(self):
pass
@abc.abstractmethod
def set_filters(self):
pass
@abc.abstractmethod
def set_sort(self):
pass
@staticmethod
@abc.abstractmethod
def get_expression_filter(col=None, op: str = 'eq', val: str = '') -> dict:
pass
@staticmethod
@abc.abstractmethod
def get_operator(op: str):
pass
def count(self):
pass
@staticmethod
@abc.abstractmethod
def generate_scope_filter(filters: list, general=False):
pass
@abc.abstractmethod
def set_joins(self):
pass
+187
View File
@@ -0,0 +1,187 @@
import datetime
from typing import Union, List
from uuid import UUID
from .AbstractEngine import AbstractEngineClass
from ..utils.Models import FilterItem, FilterScope
class MongoEngine(AbstractEngineClass):
rows = []
stack = []
filters = {}
async def exe(self):
_f = self.filters = self.set_filters()
tmp = self.query_class.find(_f).sort(**self.set_sort())
self.rows = tmp.limit(
self.input.get_limit()).skip(self.input.skip())
return [self.model(**document) async for document in self.rows], await self.count()
def query_generator(self):
if not self.input.has_filter():
return []
filters = self.input.filters()
query = self.generate_scope_filter(filters, general=True)
return query
def set_filters(self):
filters = []
query = self.query_generator()
if query:
filters.append(query)
if self.input.has_text_search():
items = []
_search_cols = self.input.get_search_cols() if (
self.input.get_search_cols() and len(self.input.get_search_cols())) else self.search_cols
if _search_cols:
for col in _search_cols:
items.append({col: {'$regex': self.input.get_text_search()}})
filters.append({"$or": items})
if self.input.extra_filter:
filters.extend(self.input.extra_filter)
if self.soft_deleted:
filters.append({'deleted_at': {'$exists': False}})
if not filters:
return {}
self.filters = {'$and': filters}
return self.filters
def set_sort(self):
if self.input.o.field:
return {'key_or_list': self.input.o.field,
'direction': 1 if self.input.o.direction == "asc" else -1}
elif self.default_sort_by:
return self.default_sort_by
else:
return {'key_or_list': 'created_at', 'direction': -1}
@staticmethod
def filter_item_generator(item: FilterItem) -> dict:
if MongoEngine.is_valid_uuid(str(item.col)):
return {
'data': {
"$elemMatch": {
'field_id': UUID(item.col),
'value': {
**MongoEngine.get_expression_filter(item.op, MongoEngine.cast_value(item.val, item.type), )}
}
}
}
return {item.col: {
**MongoEngine.get_expression_filter(col=item.col, op=item.op,
val=MongoEngine.cast_value(item.val, item.type), )}}
@staticmethod
def is_valid_uuid(uuid_to_test, version=4):
try:
uuid_obj = UUID(str(uuid_to_test), version=version)
except ValueError:
return False
return str(uuid_obj) == str(uuid_to_test)
@staticmethod
def cast_value(val: Union[str, List], type: str) -> any:
if type in ['NUMBER', 'STACK', 'FLOAT']:
if isinstance(val, List):
return [float(x) for x in val]
return float(val)
if type in ['DATE', 'DATETIME']:
if isinstance(val, List):
return [datetime.datetime.fromisoformat(str(x)) for x in val]
return datetime.datetime.fromisoformat(val)
if type == 'UUID':
if isinstance(val, List):
return [UUID(str(x)) for x in val]
return UUID(str(val))
return val
@staticmethod
def get_expression_filter(col=None, op: str = 'eq', val: str = '') -> dict:
_op = MongoEngine.get_operator(op)
if _op == "sw":
return {'$regex': f'^{val}'}
elif _op == "ew":
return {'$regex': f'{val}$'}
elif _op == "n_ew":
return {'$regex': f'(?<!{val})$'}
elif _op == "n_sw":
return {'$regex': f'^(?!{val})'}
elif _op == "n_like":
return {'$not': {'$regex': val}}
elif _op == "bet":
if isinstance(val, str):
return {'$gte': val.split('-')[0], '$lte': val.split('-')[1]}
return {'$gte': val[0], '$lte': val[1]}
elif _op == "n_bet":
if isinstance(val, str):
return {'$gte': val.split('-')[0], '$lte': val.split('-')[1]}
return {'$not': {'$gte': val[0], '$lte': val[1]}}
elif _op == "exi":
return {'$exists': True}
elif _op == "n_exi":
return {'$exists': False}
elif _op == "in":
return {'$exists': True}
elif _op == "not_in":
return {'$exists': False}
return {_op: val}
@staticmethod
def get_operator(op: str) -> str:
ops = {
"eq": "$eq",
"n_eq": "$ne",
"gt": "$gt",
"ge": "$ge",
"lt": "$lt",
"le": "$le",
"like": "$regex",
"n_like": "n_like",
"sw": "sw",
"ew": "ew",
'n_ew': 'n_ew',
'n_sw': 'n_sw',
'exi': 'exi',
"n_exi": "n_exi",
'lte': '$lte',
'gte': '$gte',
'in': 'in',
'n_in': 'not_in',
'bet': 'bet',
'n_bet': 'n_bet',
}
return ops[op] if op in ops else '$eq'
async def count(self):
return await self.query_class.count_documents(self.filters)
@staticmethod
def generate_scope_filter(filters: list, general=False):
if len(filters) == 1:
_o = filters[0]
if isinstance(_o, FilterItem):
return MongoEngine.filter_item_generator(filters[0])
elif isinstance(_o, FilterScope):
return MongoEngine.generate_scope_filter(_o.items)
else:
return _o
else:
_a = filters.pop(0)
_a = (MongoEngine.filter_item_generator(_a) if isinstance(_a,
FilterItem) else _a) if not general else (
MongoEngine.generate_scope_filter(_a.items) if isinstance(_a, FilterScope) else _a)
_b = filters.pop(0)
op = '$' + (_b.cr if not general else _b.op)
_b = (MongoEngine.filter_item_generator(_b) if isinstance(_b,
FilterItem) else _b) if not general else (
MongoEngine.generate_scope_filter(_b.items) if isinstance(_b, FilterScope) else _b)
filters = [{op: [
_a, _b
]}] + filters
return MongoEngine.generate_scope_filter(filters, general)
def set_joins(self):
pass
+146
View File
@@ -0,0 +1,146 @@
from sqlalchemy import or_, and_, desc, asc
from sqlalchemy.sql.elements import BooleanClauseList, BinaryExpression, UnaryExpression
from .AbstractEngine import AbstractEngineClass
from ..utils.Models import FilterItem, FilterScope
class MsSqlEngine(AbstractEngineClass):
def set_joins(self):
pass
filters = {}
rows = []
sort = None
async def exe(self):
_f = self.set_filters()
if not self.custom_entities:
query = self.query_class.query(self.model)
else:
query = self.query_class.query(*self.custom_entities)
if self.joins:
for j in self.joins:
if isinstance(j, list):
query = query.join(*j)
else:
query = query.join(j)
if self.options:
query = query.options(*self.options)
tmp = query.filter(_f).order_by(self.set_sort())
self.rows = tmp.limit(self.input.get_limit()).offset(self.input.skip()).all()
return self.rows, tmp.count(), tmp
def query_generator(self):
if not self.input.has_filter():
return None
filters = self.input.filters()
query = self.generate_scope_filter(filters, general=True)
return query
def set_sort(self):
if self.input.o.field:
self.sort = asc(getattr(self.model, self.input.o.field)) if self.input.o.direction == 'asc' else desc(
getattr(self.model, self.input.o.field))
return self.sort
elif isinstance(self.default_sort_by, UnaryExpression):
return self.default_sort_by
else:
return True
def set_filters(self):
filters = []
query = self.query_generator()
if isinstance(query, BooleanClauseList) or isinstance(query, BinaryExpression):
filters.append(query)
if self.input.has_text_search():
items = []
_search_cols = self.input.get_search_cols() if (
self.input.get_search_cols() and len(self.input.get_search_cols())) else self.search_cols
if _search_cols:
for col in _search_cols:
has_join = len(col.split('.')) > 1
if has_join:
pass
else:
items.append(
getattr(self.model, col).ilike("%{search}%".format(search=self.input.get_text_search())))
filters.append(or_(*items))
if self.input.extra_filter:
for f in self.input.extra_filter:
filters.append(f)
if not filters:
return True
self.filters = and_(*filters)
return self.filters
@staticmethod
def get_expression_filter(col=None, op: str = '', val: str = '') -> dict:
_op = MsSqlEngine.get_operator(op)
return getattr(col, op)(val)
@staticmethod
def get_operator(op: str):
ops = {
"eq": "__eq__",
"n_eq": "__ne__",
"gt": "__gt__",
"gte": "__ge__",
"lte": "__lte__",
"lt": "__lt__",
"like": "ilike",
"n_like": "notlike",
"in": "in_",
"n_in": "not_in",
"bet": "between",
"n_bet": "not_between",
"sw": "startswith",
"ew": "endswith",
'n_ew': 'not_endswith',
'n_sw': 'not_startswith',
'exi': 'exi',
"n_exi": "n_exi",
}
return ops[op] if op in ops else '__eq__'
@staticmethod
def get_and_or(op):
return or_ if op == 'or' else and_
def count(self):
pass
def filter_item_generator(self, item: FilterItem):
has_join = len(item.col.split('.')) > 1
if has_join:
pass
else:
return getattr(getattr(self.model, item.col), self.get_operator(item.op))(item.val)
def generate_scope_filter(self, filters: list, general=False):
if len(filters) == 1:
_o = filters[0]
if isinstance(_o, FilterItem):
return self.filter_item_generator(filters[0])
elif isinstance(_o, FilterScope):
return self.generate_scope_filter(_o.items)
else:
return _o
else:
_a = filters.pop(0)
_a = (self.filter_item_generator(_a) if isinstance(_a,
FilterItem) else _a) if not general else (
self.generate_scope_filter(_a.items) if isinstance(_a, FilterScope) else _a)
_b = filters.pop(0)
_op = MsSqlEngine.get_and_or(_b.cr if not general else _b.op)
_b = (self.filter_item_generator(_b) if isinstance(_b,
FilterItem) else _b) if not general else (
self.generate_scope_filter(_b.items) if isinstance(_b, FilterScope) else _b)
filters = [_op(_a, _b)] + filters
return self.generate_scope_filter(filters, general)
+13
View File
@@ -0,0 +1,13 @@
from .AbstractEngine import AbstractEngineClass
class PgSqlEngine(AbstractEngineClass):
def exe(self):
pass
def get_filters(self):
pass
def pagination(self):
pass
+4
View File
@@ -0,0 +1,4 @@
from enum import Enum
from .Mssql import MsSqlEngine
from .Pgsql import PgSqlEngine
from .Mongo import MongoEngine