python-general-be-lib 0.5.2__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/PKG-INFO +1 -1
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/crud_repository.py +13 -4
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/handler/__init__.py +1 -0
- python_general_be_lib-0.5.4/general/interface/repository/handler/json_ilike_handler.py +104 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/handler/statement_manager.py +5 -4
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/pyproject.toml +1 -1
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/python_general_be_lib.egg-info/PKG-INFO +1 -1
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/python_general_be_lib.egg-info/SOURCES.txt +1 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/setup.py +2 -2
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/LICENSE +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/README.md +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/__init__.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/__init__.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/access_exceptions.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/crud_exceptions.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/exception_interface.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/base/__init__.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/base/base_model.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/base/declarative_base.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/metadata/__init__.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/metadata/crud_metadata.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/metadata/geom_metadata.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/__init__.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/geometry_repository.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/handler/base_handler.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/handler/ilike_handler.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/handler/interval_handler.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/handler/json_handler.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/many_to_many_repository.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/view_repository.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/logger.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/paginator_dto.py +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/python_general_be_lib.egg-info/dependency_links.txt +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/python_general_be_lib.egg-info/requires.txt +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/python_general_be_lib.egg-info/top_level.txt +0 -0
- {python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-general-be-lib
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities.
|
|
5
5
|
Author-email: Andrea Di Placido <a.diplacido@arpes.it>, "Arpes S.r.l." <it.admin@arpes.it>
|
|
6
6
|
License: MIT
|
|
@@ -217,12 +217,13 @@ class CrudRepository[Entity: Base | Type[Base]]:
|
|
|
217
217
|
else:
|
|
218
218
|
pass
|
|
219
219
|
|
|
220
|
-
def find(self, session: Session = None, where: dict[str, Any] = None, columns: list[str] = None, exclude_columns: list[str] = None, limit: int = 0, parsing_model: Type[BaseModel] = None,
|
|
220
|
+
def find(self, session: Session = None, where: dict[str, Any] = None, columns: list[str] = None, exclude_columns: list[str] = None, limit: int = 0, parsing_model: Type[BaseModel] = None, order_by: str = None,
|
|
221
|
+
asc: bool = None, **kwhere) -> list[Entity]:
|
|
221
222
|
"""Cerca i record che corrispondono ai criteri specificati.
|
|
222
223
|
|
|
223
224
|
I filtri possono essere passati come dizionario ``where`` o come
|
|
224
225
|
keyword arguments. Supporta caricamento parziale delle colonne,
|
|
225
|
-
limit e modelli di parsing alternativi.
|
|
226
|
+
limit, ordinamento e modelli di parsing alternativi.
|
|
226
227
|
|
|
227
228
|
Args:
|
|
228
229
|
session (Session, optional): Sessione esterna. Se ``None``, ne viene
|
|
@@ -234,6 +235,10 @@ class CrudRepository[Entity: Base | Type[Base]]:
|
|
|
234
235
|
limit (int): Numero massimo di risultati. 0 = nessun limite. Default: 0.
|
|
235
236
|
parsing_model (Type[BaseModel], optional): Modello Pydantic alternativo
|
|
236
237
|
per la serializzazione del risultato.
|
|
238
|
+
order_by (str, optional): Nome della colonna per l'ordinamento dei risultati.
|
|
239
|
+
Se ``None``, non viene applicato nessun ordinamento.
|
|
240
|
+
asc (bool, optional): Direzione dell'ordinamento. ``True`` per ASC,
|
|
241
|
+
``False`` per DESC. Se ``None``, l'ordinamento di default è DESC.
|
|
237
242
|
**kwhere: Filtri aggiuntivi come keyword arguments.
|
|
238
243
|
|
|
239
244
|
Returns:
|
|
@@ -243,17 +248,21 @@ class CrudRepository[Entity: Base | Type[Base]]:
|
|
|
243
248
|
Example:
|
|
244
249
|
>>> repo.find(active=True, limit=10)
|
|
245
250
|
>>> repo.find(where={"role": ["admin", "editor"]})
|
|
251
|
+
>>> repo.find(active=True, order_by="name", asc=True)
|
|
246
252
|
"""
|
|
253
|
+
|
|
247
254
|
keep_open = False if session is None else True
|
|
248
255
|
session = self.open_session if session is None else session
|
|
249
|
-
entities = self._find(session=session, where=where, columns=columns, exclude_columns=exclude_columns, limit=limit, **kwhere)
|
|
256
|
+
entities = self._find(session=session, where=where, columns=columns, exclude_columns=exclude_columns, limit=limit, order_by=order_by, asc=asc, **kwhere)
|
|
250
257
|
return self._return(session=session, entities=entities, keep_open=keep_open, commit=False, parsing_model=parsing_model)
|
|
251
258
|
|
|
252
|
-
def _find(self, session: Session, where: dict[str, Any] = None, columns: list[str] = None, exclude_columns: list[str] = None, limit: int = 0,
|
|
259
|
+
def _find(self, session: Session, where: dict[str, Any] = None, columns: list[str] = None, exclude_columns: list[str] = None, limit: int = 0, order_by: str = None,
|
|
260
|
+
asc: bool = None, **kwhere):
|
|
253
261
|
if where is None:
|
|
254
262
|
where = kwhere if kwhere else {}
|
|
255
263
|
stmt = self._select(entity=self.entity, columns=columns, exclude_columns=exclude_columns)
|
|
256
264
|
stmt = self.stmt_manager.prepare_statement(stmt=stmt, **where)
|
|
265
|
+
stmt = self.stmt_manager.order_by_condition(stmt=stmt, order_by=order_by, asc=asc)
|
|
257
266
|
stmt = self.stmt_manager.limit_offset_condition(stmt=stmt, limit=limit)
|
|
258
267
|
return self.execute(session, stmt)
|
|
259
268
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
__all__ = ["JsonILikeHandler"]
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from general.interface.repository.handler import BaseHandler
|
|
6
|
+
from sqlalchemy import Column, Select, Update, Delete, or_
|
|
7
|
+
from sqlalchemy.sql.base import ReadOnlyColumnCollection
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class JsonILikeHandler(BaseHandler):
|
|
11
|
+
"""Handler per ricerche case-insensitive (ILIKE) su più colonne tramite un alias.
|
|
12
|
+
|
|
13
|
+
Permette di esporre un singolo parametro di ricerca testuale (es. ``"search"``)
|
|
14
|
+
che viene applicato come ``ILIKE '%value%'`` su un insieme di colonne con ``OR``.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
ilike_aliases (list[str]): Chiavi nel dizionario ``where`` che attivano
|
|
18
|
+
la ricerca ILIKE (es. ``["search", "q"]``).
|
|
19
|
+
ilike_attributes (list[str]): Nomi delle colonne su cui applicare ILIKE
|
|
20
|
+
(es. ``["name", "surname", "email"]``).
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
>>> handler = JsonILikeHandler(
|
|
24
|
+
... columns=User.columns(),
|
|
25
|
+
... ilike_aliases=["search"],
|
|
26
|
+
... ilike_attributes=["name", "surname"]
|
|
27
|
+
... )
|
|
28
|
+
>>> repo.find(where={"search": "mario"})
|
|
29
|
+
# WHERE name ILIKE '%mario%' OR surname ILIKE '%mario%'
|
|
30
|
+
"""
|
|
31
|
+
__slots__ = "ilike_aliases", "ilike_attributes", "json_column", "json_ilike_keys"
|
|
32
|
+
ilike_aliases: list[str]
|
|
33
|
+
ilike_attributes: list[str]
|
|
34
|
+
json_ilike_keys: list[str]
|
|
35
|
+
json_column: str
|
|
36
|
+
|
|
37
|
+
def __init__(self, columns: ReadOnlyColumnCollection[str, Column[Any]], ilike_aliases: list[str] = None, ilike_attributes: list[str] = None):
|
|
38
|
+
"""Inizializza l'handler validando che le colonne ILIKE esistano sull'entità.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
columns (ReadOnlyColumnCollection): Colonne della tabella.
|
|
42
|
+
ilike_aliases (list[str], optional): Chiavi alias che attivano la ricerca.
|
|
43
|
+
Default: lista vuota.
|
|
44
|
+
ilike_attributes (list[str], optional): Colonne su cui applicare ILIKE.
|
|
45
|
+
Default: lista vuota.
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
ValueError: Se uno degli ``ilike_attributes`` non è presente tra le colonne.
|
|
49
|
+
"""
|
|
50
|
+
self.columns = columns
|
|
51
|
+
self.ilike_aliases = ilike_aliases if ilike_aliases is not None else []
|
|
52
|
+
self.ilike_attributes = []
|
|
53
|
+
self.json_ilike_keys = []
|
|
54
|
+
|
|
55
|
+
if ilike_attributes:
|
|
56
|
+
for ilike_attribute in ilike_attributes:
|
|
57
|
+
if ilike_attribute not in self.columns:
|
|
58
|
+
self.json_ilike_keys.append(ilike_attribute)
|
|
59
|
+
else:
|
|
60
|
+
self.ilike_attributes.append(ilike_attribute)
|
|
61
|
+
|
|
62
|
+
for key, column in columns.items():
|
|
63
|
+
try:
|
|
64
|
+
if column.type.python_type == dict:
|
|
65
|
+
self.json_column = key
|
|
66
|
+
break
|
|
67
|
+
except NotImplementedError:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
def handle(self, stmt: Select | Update | Delete, where: dict[str, Any] = None, **kwargs):
|
|
71
|
+
"""Applica i filtri ILIKE allo statement per ogni alias trovato in ``where``.
|
|
72
|
+
|
|
73
|
+
Per ogni alias presente in ``ilike_aliases`` e trovato in ``where``,
|
|
74
|
+
estrae il valore, lo rimuove dal dizionario e aggiunge la condizione ILIKE
|
|
75
|
+
su tutte le colonne in ``ilike_attributes`` con ``OR``.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
stmt (Select | Update | Delete): Statement su cui applicare i filtri.
|
|
79
|
+
where (dict[str, Any]): Dizionario dei filtri. Gli alias ILIKE vengono
|
|
80
|
+
estratti e rimossi in-place.
|
|
81
|
+
**kwargs: Ignorati (compatibilità con l'interfaccia base).
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Select | Update | Delete: Statement con le condizioni ILIKE aggiunte.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
for alias in self.ilike_aliases:
|
|
88
|
+
condition = where.pop(alias, None)
|
|
89
|
+
if condition:
|
|
90
|
+
stmt = stmt.where(self._handle(alias, condition))
|
|
91
|
+
return stmt
|
|
92
|
+
|
|
93
|
+
def _handle(self, alias: str, condition: str, neq: bool = False):
|
|
94
|
+
"""Costruisce la condizione ILIKE con OR su tutte le colonne configurate.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
alias (str): Alias attivato (usato per identificare la ricerca, non la colonna).
|
|
98
|
+
condition (str): Stringa da cercare (viene wrappata con ``%``).
|
|
99
|
+
neq (bool): Non utilizzato in questa implementazione.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
ColumnElement: Espressione ``OR(col1 ILIKE '%val%', col2 ILIKE '%val%', ...)``.
|
|
103
|
+
"""
|
|
104
|
+
return or_(*[self.columns[col].ilike(f'%{condition}%') for col in self.ilike_attributes], *[self.columns[self.json_column][key].as_string().ilike(f'%{condition}%') for key in self.json_ilike_keys])
|
|
@@ -74,10 +74,11 @@ class StatementManager:
|
|
|
74
74
|
Raises:
|
|
75
75
|
HasNoAttributeException: Se la colonna ``order_by`` non esiste sull'entità.
|
|
76
76
|
"""
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
if order_by:
|
|
78
|
+
try:
|
|
79
|
+
stmt = stmt.order_by(self._base_handler.columns[order_by].asc()) if asc else stmt.order_by(self._base_handler.columns[order_by].desc())
|
|
80
|
+
except KeyError:
|
|
81
|
+
raise HasNoAttributeException(order_by)
|
|
81
82
|
return stmt
|
|
82
83
|
|
|
83
84
|
def limit_offset_condition(self, stmt: Select, limit: int, page: int = None):
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends.legacy:build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-general-be-lib"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.4"
|
|
8
8
|
description = "General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/python_general_be_lib.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-general-be-lib
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities.
|
|
5
5
|
Author-email: Andrea Di Placido <a.diplacido@arpes.it>, "Arpes S.r.l." <it.admin@arpes.it>
|
|
6
6
|
License: MIT
|
|
@@ -25,6 +25,7 @@ general/interface/repository/handler/base_handler.py
|
|
|
25
25
|
general/interface/repository/handler/ilike_handler.py
|
|
26
26
|
general/interface/repository/handler/interval_handler.py
|
|
27
27
|
general/interface/repository/handler/json_handler.py
|
|
28
|
+
general/interface/repository/handler/json_ilike_handler.py
|
|
28
29
|
general/interface/repository/handler/statement_manager.py
|
|
29
30
|
python_general_be_lib.egg-info/PKG-INFO
|
|
30
31
|
python_general_be_lib.egg-info/SOURCES.txt
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="python-general-be-lib",
|
|
5
|
-
version="0.5.
|
|
5
|
+
version="0.5.4",
|
|
6
6
|
packages=find_packages(include=["general*"]),
|
|
7
7
|
python_requires=">=3.12",
|
|
8
8
|
install_requires=[
|
|
@@ -11,4 +11,4 @@ setup(
|
|
|
11
11
|
"sqlalchemy>=2.0.0",
|
|
12
12
|
"geoalchemy2>=0.14.0",
|
|
13
13
|
],
|
|
14
|
-
)
|
|
14
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/access_exceptions.py
RENAMED
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/crud_exceptions.py
RENAMED
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/exception/exception_interface.py
RENAMED
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/base/__init__.py
RENAMED
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/base/base_model.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/metadata/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_general_be_lib-0.5.2 → python_general_be_lib-0.5.4}/general/interface/repository/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|