utg-base 1.2.0__py3-none-any.whl → 1.3.1__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.
utg_base/api/base.py CHANGED
@@ -3,10 +3,8 @@ from django.utils.translation import activate, get_language
3
3
  from rest_framework.request import Request
4
4
  from rest_framework.views import APIView
5
5
 
6
- from ..models import JWTUser
7
-
8
-
9
- AVAILABLE_LANGUAGES = ['uz', 'ru', 'crl']
6
+ from utg_base.constants import AVAILABLE_LANGUAGES
7
+ from utg_base.models import JWTUser
10
8
 
11
9
 
12
10
  class BaseRequest(Request):
@@ -0,0 +1,39 @@
1
+ from rest_framework import serializers
2
+ from rest_framework.exceptions import ValidationError
3
+
4
+ from utg_base.utils.date import to_udate, to_udatetime
5
+ from utg_base.utils.translation import translate as _
6
+
7
+
8
+ class UDateField(serializers.DateField):
9
+ def to_internal_value(self, value):
10
+ return to_udate(super().to_internal_value(value))
11
+
12
+
13
+ class UDateTimeField(serializers.DateTimeField):
14
+ def to_internal_value(self, value):
15
+ return to_udatetime(super().to_internal_value(value))
16
+
17
+
18
+ class DateSerializer(serializers.Serializer):
19
+ dt = UDateField()
20
+
21
+
22
+ class DatePeriodSerializer(DateSerializer):
23
+ period = serializers.ChoiceField(choices=['daily', 'monthly', 'yearly'], default='daily')
24
+
25
+
26
+ class DateTimePeriodSerializer(DateSerializer):
27
+ dt = UDateTimeField(required=False)
28
+ period = serializers.ChoiceField(choices=['hourly', 'today', 'daily', 'monthly', 'yearly'], default='daily')
29
+
30
+ def validate(self, attrs):
31
+ period = attrs.get('period', 'daily')
32
+ dt = attrs.get('dt')
33
+
34
+ if period != 'today' and dt is None:
35
+ raise ValidationError({
36
+ 'dt': _('This field is required when period is not "today".')
37
+ })
38
+ return attrs
39
+
@@ -0,0 +1 @@
1
+ from .available_languages import AVAILABLE_LANGUAGES
@@ -0,0 +1,5 @@
1
+ AVAILABLE_LANGUAGES = [
2
+ 'uz',
3
+ 'ru',
4
+ 'crl'
5
+ ]
File without changes
@@ -0,0 +1,13 @@
1
+ from django.middleware.locale import LocaleMiddleware as DjangoLocaleMiddleware
2
+ from django.utils import translation
3
+
4
+ from utg_base.constants import AVAILABLE_LANGUAGES
5
+
6
+
7
+ class LocaleMiddleware(DjangoLocaleMiddleware):
8
+ def process_request(self, request):
9
+ if lang_from_header := request.headers.get('accept-language'):
10
+ if lang_from_header and lang_from_header not in AVAILABLE_LANGUAGES:
11
+ lang_from_header = 'ru'
12
+ translation.activate(lang_from_header)
13
+ request.LANGUAGE_CODE = translation.get_language()
@@ -4,7 +4,7 @@ import requests
4
4
  import urllib3
5
5
  from requests import Response
6
6
 
7
- from utg_base.utils import call_processor
7
+ from utg_base.utils.response_processors import call_processor
8
8
 
9
9
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
10
10
 
@@ -1 +1,2 @@
1
- from .response_processors import call_processor, data_processor
1
+ from .data import deep_map, deep_round, safe_sum, safe_subtract
2
+ from .dict_util import get_by_dotted
utg_base/utils/data.py ADDED
@@ -0,0 +1,58 @@
1
+ from collections.abc import Iterable
2
+ from copy import deepcopy
3
+
4
+ from django.db.models import QuerySet
5
+
6
+
7
+ def deep_map(data: dict | list, func_cond, func_map, in_place=True):
8
+ if not in_place:
9
+ data = deepcopy(data)
10
+
11
+ if isinstance(data, dict):
12
+ for key, value in data.items():
13
+ if isinstance(value, (list, dict, QuerySet)):
14
+ deep_map(value, func_cond, func_map, True)
15
+ elif func_cond(value):
16
+ data[key] = func_map(value)
17
+ elif isinstance(data, (list, QuerySet)):
18
+ for index, value in enumerate(data):
19
+ if isinstance(value, (list, dict, QuerySet)):
20
+ deep_map(value, func_cond, func_map, True)
21
+ elif func_cond(value):
22
+ data[index] = func_map(value)
23
+
24
+ return data
25
+
26
+
27
+ def deep_round(data: dict | list, ndigits: int, in_place=True):
28
+ return deep_map(data, lambda value: isinstance(value, float), lambda value: round(value, ndigits), in_place)
29
+
30
+
31
+ def safe_sum(*args, allow_null=True):
32
+ if isinstance(args[0], Iterable):
33
+ args = args[0]
34
+
35
+ if all(arg is None for arg in args):
36
+ return None
37
+ if not allow_null and any(arg is None for arg in args):
38
+ return None
39
+
40
+ _sum = 0
41
+ for arg in args:
42
+ _sum += arg or 0
43
+ return _sum
44
+
45
+
46
+ def safe_subtract(*args, allow_null=False):
47
+ if isinstance(args[0], Iterable):
48
+ args = args[0]
49
+
50
+ if all(arg is None for arg in args):
51
+ return None
52
+ if not allow_null and any(arg is None for arg in args):
53
+ return None
54
+
55
+ _sum = args[0] or 0
56
+ for arg in args[1:]:
57
+ _sum -= arg or 0
58
+ return _sum
utg_base/utils/date.py ADDED
@@ -0,0 +1,126 @@
1
+ from calendar import monthrange
2
+ from datetime import date, datetime, timedelta
3
+
4
+ DEFAULT_DATE_FORMAT = '%Y-%m-%d'
5
+ DEFAULT_DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
6
+
7
+
8
+ def to_udate(date: date | datetime):
9
+ return UDate(date.year, date.month, date.day)
10
+
11
+
12
+ def to_udatetime(dt: datetime):
13
+ return UDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
14
+
15
+
16
+ class UDate(date):
17
+ def datetime(self):
18
+ return datetime(self.year, self.month, self.day)
19
+
20
+ def tomorrow(self):
21
+ return to_udate(self.datetime() + timedelta(days=1))
22
+
23
+ def yesterday(self):
24
+ return to_udate(self.datetime() - timedelta(days=1))
25
+
26
+ def first_day_of_month(self):
27
+ return self.replace(day=1)
28
+
29
+ def first_day_of_year(self):
30
+ return self.replace(day=1, month=1)
31
+
32
+ def last_day_of_month(self):
33
+ _, ndays = monthrange(self.year, self.month)
34
+ return self.replace(day=ndays)
35
+
36
+ def last_day_of_year(self):
37
+ return self.replace(day=31, month=12)
38
+
39
+ def first_day_of_last_month(self):
40
+ return self.replace(day=1).yesterday().replace(day=1)
41
+
42
+ def first_day_of_last_year(self):
43
+ return self.replace(day=1, month=1, year=self.year - 1)
44
+
45
+ def last_day_of_last_month(self):
46
+ return self.replace(day=1).yesterday()
47
+
48
+ def last_day_of_last_year(self):
49
+ return self.replace(day=31, month=12, year=self.year - 1)
50
+
51
+ def strftime(self, __format=DEFAULT_DATE_FORMAT):
52
+ return super().strftime(__format)
53
+
54
+ @staticmethod
55
+ def strptime(__date_string: str, __format=DEFAULT_DATE_FORMAT):
56
+ return to_udate(datetime.strptime(__date_string, __format))
57
+
58
+ def __str__(self):
59
+ return self.strftime()
60
+
61
+
62
+ class UDateTime(datetime):
63
+ def first_second_of_minute(self):
64
+ return self.replace(second=0, microsecond=0)
65
+
66
+ def last_second_of_minute(self):
67
+ return self.replace(second=59)
68
+
69
+ def first_second_of_hour(self):
70
+ return self.first_second_of_minute().replace(minute=0)
71
+
72
+ def last_second_of_hour(self):
73
+ return self.last_second_of_minute().replace(minute=59)
74
+
75
+ def first_second_of_day(self):
76
+ return self.first_second_of_hour().replace(hour=0)
77
+
78
+ def last_second_of_day(self):
79
+ return self.last_second_of_hour().replace(hour=23)
80
+
81
+ def one_hour_later(self):
82
+ return self + timedelta(hours=1)
83
+
84
+ def hour_ago(self):
85
+ return self - timedelta(hours=1)
86
+
87
+ def tomorrow(self):
88
+ return self + timedelta(days=1)
89
+
90
+ def yesterday(self):
91
+ return self - timedelta(days=1)
92
+
93
+ def first_day_of_month(self):
94
+ return self.replace(day=1)
95
+
96
+ def last_day_of_month(self):
97
+ _, ndays = monthrange(self.year, self.month)
98
+ return self.replace(day=ndays)
99
+
100
+ def first_day_of_year(self):
101
+ return self.replace(day=1, month=1)
102
+
103
+ def last_day_of_year(self):
104
+ return self.replace(day=31, month=12)
105
+
106
+ def first_day_of_last_month(self):
107
+ return self.replace(day=1).yesterday().replace(day=1)
108
+
109
+ def last_day_of_last_month(self):
110
+ return self.replace(day=1).yesterday()
111
+
112
+ def first_day_of_last_year(self):
113
+ return self.replace(day=1, month=1, year=self.year - 1)
114
+
115
+ def last_day_of_last_year(self):
116
+ return self.replace(day=31, month=12, year=self.year - 1)
117
+
118
+ def strftime(self, __format=DEFAULT_DATETIME_FORMAT):
119
+ return super().strftime(__format)
120
+
121
+ @staticmethod
122
+ def strptime(__date_string: str, __format=DEFAULT_DATETIME_FORMAT):
123
+ return to_udatetime(datetime.strptime(__date_string, __format))
124
+
125
+ def __str__(self):
126
+ return self.strftime()
@@ -0,0 +1,20 @@
1
+ def get_by_dotted(dict_obj: dict, dotted_key: str, default=None):
2
+ """
3
+ Get a value from a dictionary using a dotted key notation.
4
+
5
+ Args:
6
+ dict_obj (dict): The dictionary to retrieve the value from.
7
+ dotted_key (str): The dotted key notation to access nested keys.
8
+ default: The default value to return if the key is not found.
9
+
10
+ Returns:
11
+ The value at the given dotted key, or default if not found.
12
+ """
13
+ parts = dotted_key.split('.')
14
+ for part in parts:
15
+ # Check if current part is in the dictionary and is a dictionary itself
16
+ if isinstance(dict_obj, dict) and part in dict_obj:
17
+ dict_obj = dict_obj.get(part, default)
18
+ else:
19
+ return default
20
+ return dict_obj
utg_base/utils/sql.py ADDED
@@ -0,0 +1,25 @@
1
+ from django.db import connection
2
+
3
+
4
+ def fetchone(query: str, params=None, desc=True):
5
+ if params is None:
6
+ params = []
7
+ with connection.cursor() as cursor:
8
+ cursor.execute(query, params)
9
+ rows = cursor.fetchone()
10
+ if not desc or not rows:
11
+ return rows
12
+ columns = [desc[0] for desc in cursor.description]
13
+ return dict(zip(columns, rows))
14
+
15
+
16
+ def fetchall(query: str, params=None, desc=True):
17
+ if params is None:
18
+ params = []
19
+ with connection.cursor() as cursor:
20
+ cursor.execute(query, params)
21
+ rows = cursor.fetchall()
22
+ if not desc:
23
+ return rows
24
+ columns = [desc[0] for desc in cursor.description]
25
+ return [dict(zip(columns, row)) for row in rows]
@@ -0,0 +1,18 @@
1
+ import os
2
+
3
+ import requests
4
+ from django.utils.translation import get_language
5
+
6
+ from utg_base.constants import AVAILABLE_LANGUAGES
7
+
8
+
9
+ def translate(key: str) -> str:
10
+ base_url = os.environ.get('TRANSLATION_SERVICE_URL')
11
+ lang = get_language()
12
+ if lang == 'uz-cyr':
13
+ lang = 'crl'
14
+ if lang not in AVAILABLE_LANGUAGES:
15
+ lang = 'ru'
16
+ return requests.get(f'{base_url}/api/translations/{key}', params={
17
+ 'lang': lang
18
+ }).text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utg-base
3
- Version: 1.2.0
3
+ Version: 1.3.1
4
4
  Summary: UTG Base Package
5
5
  Author: Rovshen
6
6
  Author-email: rovshenashirov1619@gmail.com
@@ -1,13 +1,18 @@
1
1
  utg_base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  utg_base/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- utg_base/api/base.py,sha256=gbL3TtJKqdstpfbycvHorqpWaVRI87C1zix_MrJn0Cc,2560
3
+ utg_base/api/base.py,sha256=giGKC68sL1j1NQbbkhIWzhHPcPlvjTPG2evra-xAJ3s,2574
4
4
  utg_base/api/pagination.py,sha256=zzIUwW3iF5G_11gFsno9y1DmgFiULQIWRHUj0LKhYfE,854
5
5
  utg_base/api/routers.py,sha256=lU54MVN2BF_q1AWp9EdXkG3m_ivYRtvbNGXFIRKz7u0,177
6
+ utg_base/api/serializers.py,sha256=qI6wWjwl1oeUPHCJUCpYFIaiRFvfQW6FM0xPC9fwfQI,1214
6
7
  utg_base/api/views.py,sha256=yYCEJRouFA71cI2Ubc1A736oLg9NGWyTIVnD-Q85k6w,279
7
8
  utg_base/authentications/__init__.py,sha256=a6twO_bBf8FAHYl7PXawfR2UbBwwdueG1uS_dbq2g_I,109
8
9
  utg_base/authentications/microservice_authentication.py,sha256=6aAncxIpA4FcyRegd7QqRYvW5Wn8FxyPU0nQqCVuEs4,976
9
10
  utg_base/authentications/models.py,sha256=JQonSdXeSeoF003QlmPvH58nWmVJRKlWWjW_ySqXaYg,2496
11
+ utg_base/constants/__init__.py,sha256=nC8qE-2V6APtjSz8j0A-3ez8yyoRpdRO8pwQnvvpRMk,53
12
+ utg_base/constants/available_languages.py,sha256=zQh0S0PMuYUdRW_RH36llvMxbvsfbdUtotDjFeysWfQ,56
10
13
  utg_base/logging.py,sha256=MNPUQ9Hdg-0YTFlJBOwhajqKz-8Y5P4POkqRNIU9ga8,5078
14
+ utg_base/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ utg_base/middleware/locale.py,sha256=1hp_T_VuHCz0ITjwJ_F1rpf5kXQ0ulEmK1yoRWD1GRc,557
11
16
  utg_base/models/__init__.py,sha256=ylLp_n2WJqvmuG-4X8yv119Ikcx-OFSYHUcnggUeQeo,30
12
17
  utg_base/models/jwt_user.py,sha256=6TQ5wB_OZBtGhRL-2MonBGZm0n0Y86s4BRTxiRlUJOk,375
13
18
  utg_base/references_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -18,9 +23,14 @@ utg_base/references_api/models.py,sha256=Vjc0p2XbAPgE6HyTF6vll98A4eDhA5AvaQqsc4k
18
23
  utg_base/references_api/urls.py,sha256=WkLACQ8GfK5pJkvt8FuYdOxcqSZHj7pYRw51M9WluGw,390
19
24
  utg_base/references_api/utils.py,sha256=VzQMnGeWC0I-6BkK_9Lo9eSH6wd4C766oQ84n7SBWyc,5818
20
25
  utg_base/services/__init__.py,sha256=LqtwUiqEZPIbKRGJfve5D5m3ucV6Kw1Nbo5Jnj_hPhY,37
21
- utg_base/services/base_api.py,sha256=fxwy7VagfyngvcbNdk7b3G5XOyykaJQQyDMub1EyCLA,5187
22
- utg_base/utils/__init__.py,sha256=wpQYZVF_uF7tVm3tPkyyvZPibPMRKaG4ebe3WPA68uc,63
26
+ utg_base/services/base_api.py,sha256=RBwzO6frYs2TeMKkAohUxtH9JhDTgmb9G5BFijajg68,5207
27
+ utg_base/utils/__init__.py,sha256=5XmIPVpOl9Tjtzkx_bBeZD1uCpBE-R3WX6yiJii9Ip0,101
28
+ utg_base/utils/data.py,sha256=XmGJStl2f5Bx2B5F45KOUlyS7JkxYiVdWu66_RA_sWQ,1635
29
+ utg_base/utils/date.py,sha256=thcbK6RgTUYZfs4_vW5ucuu2e8H0rei6tv7SEC72iwM,3612
30
+ utg_base/utils/dict_util.py,sha256=ipdCZO8aTukGQ319OWHb2Ij5MNtV-FioJQ4qCX3Th48,758
23
31
  utg_base/utils/response_processors.py,sha256=Eim0NIbCt2GH_grzrD6DHqr5iczCHfu1wr9lqThlX3U,605
24
- utg_base-1.2.0.dist-info/METADATA,sha256=ta_caUY5Jm94LtCn66BJynhRxHUyt99l-JG2hj2HvpY,660
25
- utg_base-1.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- utg_base-1.2.0.dist-info/RECORD,,
32
+ utg_base/utils/sql.py,sha256=rqIWcSjdjIMszdRnsnhV5TTYB8W17RPOujIQA9rKC_Y,762
33
+ utg_base/utils/translation.py,sha256=HAUB64h0Maw82ehCoi0Yb6V6gj1Y5l5RMsv8_FMoV2U,456
34
+ utg_base-1.3.1.dist-info/METADATA,sha256=_VWqPBkJ0EoajvscvSsD_68Pm4siuvCLFDKgyCd7nWI,660
35
+ utg_base-1.3.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
+ utg_base-1.3.1.dist-info/RECORD,,