maleo-foundation 0.0.45__py3-none-any.whl → 0.0.47__py3-none-any.whl

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.
maleo_foundation/query.py CHANGED
@@ -9,6 +9,20 @@ from maleo_foundation.types import BaseTypes
9
9
  from maleo_foundation.extended_types import ExtendedTypes
10
10
 
11
11
  class BaseQuery:
12
+ @staticmethod
13
+ def filter_column(query:Query, table:Type[DatabaseManager.Base], column:str, value:BaseTypes.OptionalAny) -> Query:
14
+ if not value:
15
+ return query
16
+ column_attr = getattr(table, column, None)
17
+ if not column_attr:
18
+ return query
19
+ if isinstance(value, list):
20
+ value_filters = [column_attr == val for val in value]
21
+ query = query.filter(or_(*value_filters))
22
+ return query
23
+ query = query.filter(column_attr == value)
24
+ return query
25
+
12
26
  @staticmethod
13
27
  def filter_ids(query:Query, table:Type[DatabaseManager.Base], column:str, ids:BaseTypes.OptionalListOfIntegers) -> Query:
14
28
  if ids is not None:
@@ -2,8 +2,12 @@ from __future__ import annotations
2
2
  from .formatter import BaseFormatter
3
3
  from .logger import BaseLogger
4
4
  from .exceptions import BaseExceptions
5
+ from .controller import BaseControllerUtils
6
+ from .query import BaseQueryUtils
5
7
 
6
8
  class BaseUtils:
7
9
  Formatter = BaseFormatter
8
10
  Logger = BaseLogger
9
- Exceptions = BaseExceptions
11
+ Exceptions = BaseExceptions
12
+ Controller = BaseControllerUtils
13
+ Query = BaseQueryUtils
@@ -0,0 +1,50 @@
1
+ from fastapi import status
2
+ from typing import Type, Any
3
+ from maleo_foundation.enums import BaseEnums
4
+ from maleo_foundation.models.responses import BaseResponses
5
+ from maleo_foundation.models.transfers.results.service.controllers.rest import BaseServiceRESTControllerResults
6
+ from maleo_foundation.models.transfers.parameters.service import BaseServiceParametersTransfers
7
+ from maleo_foundation.expanded_types.service import ExpandedServiceTypes
8
+
9
+ class BaseControllerUtils:
10
+ @staticmethod
11
+ def check_unique_existence(
12
+ check:BaseServiceParametersTransfers.UniqueFieldCheck,
13
+ get_single_parameters_class:Type[ExpandedServiceTypes.GetSingleParameter],
14
+ get_single_service_function:ExpandedServiceTypes.SyncGetSingleFunction,
15
+ create_failed_response_class:Type[BaseResponses.Fail],
16
+ update_failed_response_class:Type[BaseResponses.Fail],
17
+ **additional_get_parameters:Any
18
+ ) -> BaseServiceRESTControllerResults:
19
+ """Generic helper function to check if a unique value exists in the database."""
20
+
21
+ #* Return early if nullable and no new value
22
+ if check.nullable and check.new_value is None:
23
+ return BaseServiceRESTControllerResults(success=True, content=None)
24
+
25
+ #* Return early if values are unchanged on update
26
+ if check.operation == BaseEnums.OperationType.UPDATE and check.old_value == check.new_value:
27
+ return BaseServiceRESTControllerResults(success=True, content=None)
28
+
29
+ #* Prepare parameters to query for existing data
30
+ get_single_parameters = get_single_parameters_class(identifier=check.field, value=check.new_value)
31
+
32
+ #* Query the existing data using provided function
33
+ service_result:ExpandedServiceTypes.GetSingleResult = get_single_service_function(parameters=get_single_parameters, **additional_get_parameters)
34
+ if not service_result.success:
35
+ content = BaseResponses.ServerError.model_validate(service_result.model_dump(exclude_unset=True)).model_dump()
36
+ return BaseServiceRESTControllerResults(success=False, content=content, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
37
+
38
+ #* Handle case if duplicate is found
39
+ if service_result.data:
40
+ description = f"External error: {check.field} of '{check.new_value}' already exists in the database"
41
+ other = check.suggestion or f"Select another {check.field} value"
42
+ if check.operation == BaseEnums.OperationType.CREATE:
43
+ content = create_failed_response_class(description=description, other=other).model_dump()
44
+ elif check.operation == BaseEnums.OperationType.UPDATE:
45
+ content = update_failed_response_class(description=description, other=other).model_dump()
46
+
47
+ return BaseServiceRESTControllerResults(success=False, content=content, status_code=status.HTTP_400_BAD_REQUEST)
48
+
49
+ #* No duplicates found
50
+ return BaseServiceRESTControllerResults(success=True, content=None)
@@ -0,0 +1,95 @@
1
+ from sqlalchemy import Column, Table
2
+ from sqlalchemy.orm import Query
3
+ from sqlalchemy.orm.attributes import InstrumentedAttribute
4
+ from sqlalchemy.sql.expression import or_, asc, cast, desc
5
+ from sqlalchemy.types import DATE, String, TEXT, TIMESTAMP
6
+ from typing import Type
7
+ from maleo_foundation.db import DatabaseManager
8
+ from maleo_foundation.types import BaseTypes
9
+ from maleo_foundation.extended_types import ExtendedTypes
10
+
11
+ class BaseQueryUtils:
12
+ @staticmethod
13
+ def filter_column(query:Query, table:Type[DatabaseManager.Base], column:str, value:BaseTypes.OptionalAny) -> Query:
14
+ if not value:
15
+ return query
16
+ column_attr = getattr(table, column, None)
17
+ if not column_attr:
18
+ return query
19
+ if isinstance(value, list):
20
+ value_filters = [column_attr == val for val in value]
21
+ query = query.filter(or_(*value_filters))
22
+ return query
23
+ query = query.filter(column_attr == value)
24
+ return query
25
+
26
+ @staticmethod
27
+ def filter_ids(query:Query, table:Type[DatabaseManager.Base], column:str, ids:BaseTypes.OptionalListOfIntegers) -> Query:
28
+ if ids is not None:
29
+ column_attr = getattr(table, column, None)
30
+ if column_attr:
31
+ id_filters = [column_attr == id for id in ids]
32
+ query = query.filter(or_(*id_filters))
33
+ return query
34
+
35
+ @staticmethod
36
+ def filter_timestamps(query:Query, table:Type[DatabaseManager.Base], date_filters:ExtendedTypes.ListOfDateFilters) -> Query:
37
+ if date_filters and len(date_filters) > 0:
38
+ for date_filter in date_filters:
39
+ try:
40
+ table:Table = table.__table__
41
+ column:Column = table.columns[date_filter.name]
42
+ column_attr:InstrumentedAttribute = getattr(table, date_filter.name)
43
+ if isinstance(column.type, (TIMESTAMP, DATE)):
44
+ if date_filter.from_date and date_filter.to_date:
45
+ query = query.filter(column_attr.between(date_filter.from_date, date_filter.to_date))
46
+ elif date_filter.from_date:
47
+ query = query.filter(column_attr >= date_filter.from_date)
48
+ elif date_filter.to_date:
49
+ query = query.filter(column_attr <= date_filter.to_date)
50
+ except KeyError:
51
+ continue
52
+ return query
53
+
54
+ @staticmethod
55
+ def filter_statuses(query:Query, table:Type[DatabaseManager.Base], statuses:BaseTypes.OptionalListOfStatuses) -> Query:
56
+ if statuses is not None:
57
+ status_filters = [table.status == status for status in statuses]
58
+ query = query.filter(or_(*status_filters))
59
+ return query
60
+
61
+ @staticmethod
62
+ def filter_search(query:Query, table:Type[DatabaseManager.Base], search:BaseTypes.OptionalString) -> Query:
63
+ if search:
64
+ search_term = f"%{search}%" #* Use wildcard for partial matching
65
+ search_filters = []
66
+ sqla_table:Table = table.__table__
67
+ for name, attr in vars(table).items():
68
+ try:
69
+ column: Column = sqla_table.columns[name]
70
+ if not isinstance(attr, InstrumentedAttribute):
71
+ continue
72
+ if isinstance(column.type, (String, TEXT)):
73
+ search_filters.append(cast(attr, TEXT).ilike(search_term))
74
+ except KeyError:
75
+ continue
76
+ if search_filters:
77
+ query = query.filter(or_(*search_filters))
78
+ return query
79
+
80
+ @staticmethod
81
+ def sort(query:Query, table:Type[DatabaseManager.Base], sort_columns:ExtendedTypes.ListOfSortColumns) -> Query:
82
+ for sort_column in sort_columns:
83
+ try:
84
+ sort_col = getattr(table, sort_column.name)
85
+ sort_col = asc(sort_col) if sort_column.order.value.lower() == "asc" else desc(sort_col)
86
+ query = query.order_by(sort_col)
87
+ except AttributeError:
88
+ continue
89
+ return query
90
+
91
+ @staticmethod
92
+ def paginate(query:Query, page:int, limit:int) -> Query:
93
+ offset:int = (page - 1) * limit #* Calculate offset based on page
94
+ query = query.limit(limit=limit).offset(offset=offset)
95
+ return query
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maleo_foundation
3
- Version: 0.0.45
3
+ Version: 0.0.47
4
4
  Summary: Foundation package for Maleo
5
5
  Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
6
  License: MIT
@@ -3,7 +3,7 @@ maleo_foundation/constants.py,sha256=aBmEfWlBqZxi0k-n6h2NM1YRLOjMnheEiLyQcjP-zCQ
3
3
  maleo_foundation/controller.py,sha256=9ceIEajWLouLBph2NQe7L7tQNnQyyW_ETojilSeFv6M,2991
4
4
  maleo_foundation/enums.py,sha256=uXA5fLFeDXwlspUjU4DXSB68dfanxEwIJhsAMZXhsYo,2359
5
5
  maleo_foundation/extended_types.py,sha256=pIKt-_9tby4rmune3fmWcCW_mohaNRh_1lywBmdc-L4,301
6
- maleo_foundation/query.py,sha256=qJV9-QX5bphsmijXf4I8cuwMnqlJkm0dxypJ9Qz1eS8,3850
6
+ maleo_foundation/query.py,sha256=O8U-RknKlihONxnjlKLQ02Q2aarH2GzEf2T4VABCMe4,4393
7
7
  maleo_foundation/types.py,sha256=D_EtYK-sNeELSbc-ngX65iuQCRhZQB4TtV73P6Gl76o,1206
8
8
  maleo_foundation/clients/__init__.py,sha256=W8vydJYeDEi6gdmOZSBFSSDsfZJtb8C05CHErZgsZ30,188
9
9
  maleo_foundation/clients/logging.py,sha256=JaAYfLkci97bTb6Wt5YO1uxQK5afIMuR8kin0q5rlIM,1145
@@ -46,12 +46,14 @@ maleo_foundation/models/transfers/results/service/general.py,sha256=G4x-MhQI7Km9
46
46
  maleo_foundation/models/transfers/results/service/query.py,sha256=Wj9GCWk7FrKP0EoK55vJcr9YJ42Lo24mLXbRk9j0IJw,2566
47
47
  maleo_foundation/models/transfers/results/service/controllers/__init__.py,sha256=HZJWMy2dskzOCzLmp_UaL9rjbQ-sDMI7sd2bXb-4QOU,175
48
48
  maleo_foundation/models/transfers/results/service/controllers/rest.py,sha256=wCuFyOTQkuBs2cqjPsWnPy0XIsCfMqGByhrSy57qp7Y,1107
49
- maleo_foundation/utils/__init__.py,sha256=tfgaHZI2PDgxEVSQztfnDMN5S6L5Y4FcK5v_Wkf5snE,245
49
+ maleo_foundation/utils/__init__.py,sha256=FavmL5XYGCm955EAKiWWcXYeU15p5rSzfcglpV2yI6c,387
50
+ maleo_foundation/utils/controller.py,sha256=hapDGng4q5XTfaG18M3bbFsFGqEOlkNKRzGf8BMKrhs,2996
50
51
  maleo_foundation/utils/exceptions.py,sha256=mcvBwHm6uWpVQkPtO1T2j-GaTYEiyPOeGxiDL9-sjNA,3639
51
52
  maleo_foundation/utils/logger.py,sha256=sK95UC5jLKKuAV8lu8pFnoQfLRB0nPtCbmAC5CJLgO8,3338
53
+ maleo_foundation/utils/query.py,sha256=qKNcjf5J7_Q7NjA79BO6_TpZVdYcF8YIO68F9M501ew,4398
52
54
  maleo_foundation/utils/formatter/__init__.py,sha256=iKf5YCbEdg1qKnFHyKqqcQbqAqEeRUf8mhI3v3dQoj8,78
53
55
  maleo_foundation/utils/formatter/case.py,sha256=TmvvlfzGdC_omMTB5vAa40TZBxQ3hnr-SYeo0M52Rlg,1352
54
- maleo_foundation-0.0.45.dist-info/METADATA,sha256=o37CqDWclV1BNBghPffqhbQAYP7WMnIIZug2HEujbj8,3160
55
- maleo_foundation-0.0.45.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
56
- maleo_foundation-0.0.45.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
57
- maleo_foundation-0.0.45.dist-info/RECORD,,
56
+ maleo_foundation-0.0.47.dist-info/METADATA,sha256=u2R6BiNSqzVrxOPsFQgRx9WsB15xvdmatl0NRvd6Eqw,3160
57
+ maleo_foundation-0.0.47.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
58
+ maleo_foundation-0.0.47.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
59
+ maleo_foundation-0.0.47.dist-info/RECORD,,