python-general-be-lib 0.5.0__tar.gz → 0.5.2__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.
Files changed (47) hide show
  1. {python_general_be_lib-0.5.0/python_general_be_lib.egg-info → python_general_be_lib-0.5.2}/PKG-INFO +1 -1
  2. python_general_be_lib-0.5.2/general/interface/base/base_model.py +64 -0
  3. python_general_be_lib-0.5.2/general/interface/base/declarative_base.py +144 -0
  4. python_general_be_lib-0.5.2/general/interface/repository/crud_repository.py +607 -0
  5. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/repository/geometry_repository.py +114 -2
  6. python_general_be_lib-0.5.2/general/interface/repository/handler/base_handler.py +68 -0
  7. python_general_be_lib-0.5.2/general/interface/repository/handler/ilike_handler.py +90 -0
  8. python_general_be_lib-0.5.2/general/interface/repository/handler/interval_handler.py +92 -0
  9. python_general_be_lib-0.5.2/general/interface/repository/handler/json_handler.py +71 -0
  10. python_general_be_lib-0.5.2/general/interface/repository/handler/statement_manager.py +103 -0
  11. python_general_be_lib-0.5.2/general/interface/repository/many_to_many_repository.py +184 -0
  12. python_general_be_lib-0.5.2/general/interface/repository/view_repository.py +120 -0
  13. python_general_be_lib-0.5.2/general/logger.py +135 -0
  14. python_general_be_lib-0.5.2/general/paginator_dto.py +74 -0
  15. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/pyproject.toml +1 -1
  16. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2/python_general_be_lib.egg-info}/PKG-INFO +1 -1
  17. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/setup.py +1 -1
  18. python_general_be_lib-0.5.0/general/interface/base/base_model.py +0 -27
  19. python_general_be_lib-0.5.0/general/interface/base/declarative_base.py +0 -57
  20. python_general_be_lib-0.5.0/general/interface/repository/crud_repository.py +0 -276
  21. python_general_be_lib-0.5.0/general/interface/repository/handler/base_handler.py +0 -23
  22. python_general_be_lib-0.5.0/general/interface/repository/handler/ilike_handler.py +0 -33
  23. python_general_be_lib-0.5.0/general/interface/repository/handler/interval_handler.py +0 -33
  24. python_general_be_lib-0.5.0/general/interface/repository/handler/json_handler.py +0 -29
  25. python_general_be_lib-0.5.0/general/interface/repository/handler/statement_manager.py +0 -40
  26. python_general_be_lib-0.5.0/general/interface/repository/many_to_many_repository.py +0 -101
  27. python_general_be_lib-0.5.0/general/interface/repository/view_repository.py +0 -57
  28. python_general_be_lib-0.5.0/general/logger.py +0 -76
  29. python_general_be_lib-0.5.0/general/paginator_dto.py +0 -23
  30. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/LICENSE +0 -0
  31. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/README.md +0 -0
  32. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/__init__.py +0 -0
  33. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/exception/__init__.py +0 -0
  34. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/exception/access_exceptions.py +0 -0
  35. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/exception/crud_exceptions.py +0 -0
  36. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/exception/exception_interface.py +0 -0
  37. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/base/__init__.py +0 -0
  38. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/metadata/__init__.py +0 -0
  39. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/metadata/crud_metadata.py +0 -0
  40. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/metadata/geom_metadata.py +0 -0
  41. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/repository/__init__.py +0 -0
  42. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/general/interface/repository/handler/__init__.py +0 -0
  43. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/python_general_be_lib.egg-info/SOURCES.txt +0 -0
  44. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/python_general_be_lib.egg-info/dependency_links.txt +0 -0
  45. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/python_general_be_lib.egg-info/requires.txt +0 -0
  46. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/python_general_be_lib.egg-info/top_level.txt +0 -0
  47. {python_general_be_lib-0.5.0 → python_general_be_lib-0.5.2}/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.0
3
+ Version: 0.5.2
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
@@ -0,0 +1,64 @@
1
+ __all__ = ["ExtBaseModel", "CamelExtBaseModel"]
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+ from pydantic.alias_generators import to_camel
5
+
6
+
7
+ class ConfigModel(BaseModel):
8
+ """Modello Pydantic base con configurazione condivisa.
9
+
10
+ Abilita:
11
+ - ``from_attributes=True``: validazione da oggetti ORM SQLAlchemy.
12
+ - ``populate_by_name=True``: accetta sia il nome Python che l'alias.
13
+ - ``arbitrary_types_allowed=True``: consente tipi non standard (es. ``WKBElement``).
14
+ - ``validate_default=True``: valida i valori di default alla creazione.
15
+ """
16
+ model_config = ConfigDict(from_attributes=True, populate_by_name=True, arbitrary_types_allowed=True, validate_default=True)
17
+
18
+
19
+ class CamelConfigModel(ConfigModel):
20
+ """Estende ``ConfigModel`` aggiungendo la generazione automatica di alias camelCase."""
21
+ model_config = ConfigDict(alias_generator=to_camel)
22
+
23
+ @classmethod
24
+ def get_attr_by_alias(cls, alias: str) -> str:
25
+ """Restituisce il nome del campo Python dato un alias camelCase.
26
+
27
+ Utile per risalire al nome interno del campo quando si riceve
28
+ un payload con chiavi in camelCase.
29
+
30
+ Args:
31
+ alias (str): Alias camelCase del campo (es. ``"myField"``).
32
+
33
+ Returns:
34
+ str: Nome Python del campo (es. ``"my_field"``),
35
+ oppure l'alias stesso se non trovato.
36
+
37
+ Example:
38
+ >>> class MyModel(CamelExtBaseModel):
39
+ ... my_field: str
40
+ >>> MyModel.get_attr_by_alias("myField")
41
+ 'my_field'
42
+ """
43
+ for name, field in cls.model_fields.items():
44
+ if field.alias == alias:
45
+ return name
46
+ return alias
47
+
48
+
49
+ class ExtBaseModel(ConfigModel):
50
+ """Modello Pydantic con configurazione standard per l'integrazione ORM.
51
+
52
+ Eredita da ``ConfigModel``. Da usare come base per tutti i modelli
53
+ che devono essere validati a partire da entità SQLAlchemy.
54
+ """
55
+ pass
56
+
57
+
58
+ class CamelExtBaseModel(CamelConfigModel):
59
+ """Modello Pydantic con alias camelCase automatici e integrazione ORM.
60
+
61
+ Eredita da ``CamelConfigModel``. Da usare quando l'API espone
62
+ campi in camelCase ma internamente si lavora con snake_case.
63
+ """
64
+ pass
@@ -0,0 +1,144 @@
1
+ __all__ = ["Base", "retrieve_mapper_entity", "retrieve_mapper_from_table"]
2
+
3
+ from typing import Optional, Any
4
+
5
+ from geoalchemy2 import Geometry, WKBElement
6
+ from pydantic import create_model
7
+ from sqlalchemy.inspection import inspect
8
+ from sqlalchemy.orm import Mapper, as_declarative
9
+
10
+
11
+ @as_declarative()
12
+ class Base:
13
+ """Classe base dichiarativa SQLAlchemy arricchita con metodi di utilità.
14
+
15
+ Tutte le entità ORM del progetto devono estendere questa classe.
16
+ Fornisce accesso rapido a colonne, chiavi primarie e conversione verso Pydantic.
17
+ """
18
+
19
+ @classmethod
20
+ def to_pydantic(cls, model_name: str = None, full_optional: bool = False, default_values: dict[str, Any] = None, **create_model_kwargs):
21
+ """Genera dinamicamente un modello Pydantic a partire dalle colonne dell'entità.
22
+
23
+ Itera sulle colonne mappate dall'ORM e costruisce i campi del modello
24
+ rispettando la nullabilità e i valori di default forniti.
25
+ Le colonne di tipo ``Geometry`` vengono mappate su ``WKBElement``.
26
+
27
+ Args:
28
+ model_name (str, optional): Nome del modello generato.
29
+ Se non fornito, viene usato ``{ClassName}Model``.
30
+ full_optional (bool): Se ``True``, tutti i campi diventano ``Optional``
31
+ con default ``None``, indipendentemente dalla nullabilità nel DB.
32
+ Default: ``False``.
33
+ default_values (dict[str, Any], optional): Dizionario
34
+ ``{nome_campo: valore_default}`` per sovrascrivere i default inferiti.
35
+ **create_model_kwargs: Argomenti aggiuntivi passati a ``pydantic.create_model``.
36
+
37
+ Returns:
38
+ Type[BaseModel]: Classe Pydantic generata dinamicamente.
39
+
40
+ Example:
41
+ >>> UserModel = User.to_pydantic(full_optional=True)
42
+ >>> PatchModel = User.to_pydantic(model_name="UserPatch", full_optional=True)
43
+ """
44
+ if not default_values:
45
+ default_values = dict()
46
+ mapper: Mapper = inspect(cls)
47
+ model_name = f"{cls.__name__}Model" if not model_name else model_name
48
+ fields = dict()
49
+ field_values = {col.name: None if full_optional else ... for col in mapper.columns}
50
+ field_values.update(default_values)
51
+ for col in mapper.columns:
52
+ field_name = col.name
53
+ if isinstance(col.type, Geometry):
54
+ field_type = Optional[WKBElement] if col.nullable or full_optional else WKBElement
55
+ else:
56
+ field_type = Optional[col.type.python_type] if col.nullable or full_optional else col.type.python_type
57
+ fields[field_name] = (field_type, field_values[field_name])
58
+ return create_model(model_name, **create_model_kwargs, **fields)
59
+
60
+ @classmethod
61
+ def columns(cls):
62
+ """Restituisce la collezione di colonne della tabella associata all'entità.
63
+
64
+ Returns:
65
+ ReadOnlyColumnCollection: Collezione delle colonne SQLAlchemy.
66
+
67
+ Example:
68
+ >>> User.columns()
69
+ """
70
+ return cls.__table__.c
71
+
72
+ @classmethod
73
+ def columns_names(cls):
74
+ """Restituisce i nomi di tutte le colonne della tabella.
75
+
76
+ Returns:
77
+ list[str]: Lista dei nomi delle colonne nell'ordine di definizione.
78
+
79
+ Example:
80
+ >>> User.columns_names()
81
+ ['id', 'name', 'email']
82
+ """
83
+ return [col.name for col in cls.__table__.c]
84
+
85
+ @classmethod
86
+ def primary_key(cls):
87
+ """Restituisce le colonne che compongono la chiave primaria della tabella.
88
+
89
+ Returns:
90
+ list[Column]: Lista delle colonne che formano la primary key.
91
+
92
+ Example:
93
+ >>> User.primary_key()
94
+ [Column('id', Integer(), ...)]
95
+ """
96
+ return cls.__table__.primary_key.columns.values()
97
+
98
+ def to_dict(self):
99
+ """Converte l'istanza in un dizionario ``{colonna: valore}``.
100
+
101
+ Returns:
102
+ dict[str, Any]: Dizionario con i valori correnti dell'istanza.
103
+
104
+ Example:
105
+ >>> user.to_dict()
106
+ {'id': 1, 'name': 'Mario', 'email': 'mario@example.com'}
107
+ """
108
+ return {key: getattr(self, key) for key in self.__class__.columns_names()}
109
+
110
+
111
+ def retrieve_mapper_entity(entity_name: str):
112
+ """Cerca e restituisce una sottoclasse di ``Base`` per nome della classe.
113
+
114
+ Args:
115
+ entity_name (str): Nome della classe ORM da cercare (es. ``"User"``).
116
+
117
+ Returns:
118
+ Type[Base] | None: La classe ORM corrispondente, o ``None`` se non trovata.
119
+
120
+ Example:
121
+ >>> retrieve_mapper_entity("User").__tablename__
122
+ 'users'
123
+ """
124
+ for mapper in Base.__subclasses__():
125
+ if mapper.__name__ == entity_name:
126
+ return mapper
127
+
128
+
129
+ def retrieve_mapper_from_table(table_name: str):
130
+ """Cerca e restituisce una sottoclasse di ``Base`` per nome della tabella.
131
+
132
+ Args:
133
+ table_name (str): Nome della tabella nel database (es. ``"users"``).
134
+
135
+ Returns:
136
+ Type[Base] | None: La classe ORM corrispondente, o ``None`` se non trovata.
137
+
138
+ Example:
139
+ >>> retrieve_mapper_from_table("users").__name__
140
+ 'User'
141
+ """
142
+ for mapper in Base.__subclasses__():
143
+ if mapper.__tablename__ == table_name:
144
+ return mapper