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 +2 -4
- utg_base/api/serializers.py +39 -0
- utg_base/constants/__init__.py +1 -0
- utg_base/constants/available_languages.py +5 -0
- utg_base/middleware/__init__.py +0 -0
- utg_base/middleware/locale.py +13 -0
- utg_base/services/base_api.py +1 -1
- utg_base/utils/__init__.py +2 -1
- utg_base/utils/data.py +58 -0
- utg_base/utils/date.py +126 -0
- utg_base/utils/dict_util.py +20 -0
- utg_base/utils/sql.py +25 -0
- utg_base/utils/translation.py +18 -0
- {utg_base-1.2.0.dist-info → utg_base-1.3.1.dist-info}/METADATA +1 -1
- {utg_base-1.2.0.dist-info → utg_base-1.3.1.dist-info}/RECORD +16 -6
- {utg_base-1.2.0.dist-info → utg_base-1.3.1.dist-info}/WHEEL +0 -0
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
|
|
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
|
|
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()
|
utg_base/services/base_api.py
CHANGED
utg_base/utils/__init__.py
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
from .
|
|
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,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=
|
|
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=
|
|
22
|
-
utg_base/utils/__init__.py,sha256=
|
|
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
|
|
25
|
-
utg_base
|
|
26
|
-
utg_base-1.
|
|
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,,
|
|
File without changes
|