utg-base 1.0.1__py3-none-any.whl → 1.1.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 ADDED
@@ -0,0 +1,75 @@
1
+ from django.utils import timezone
2
+ from django.utils.translation import activate, get_language
3
+ from rest_framework.request import Request
4
+ from rest_framework.views import APIView
5
+
6
+ from ..models import JWTUser
7
+
8
+
9
+ AVAILABLE_LANGUAGES = ['uz', 'ru', 'crl']
10
+
11
+
12
+ class BaseRequest(Request):
13
+
14
+ @property
15
+ def user(self) -> JWTUser:
16
+ return super(BaseRequest, self).user()
17
+
18
+
19
+ class BaseAPIView(APIView):
20
+ user_id_assign_fields_on_create = []
21
+ user_id_assign_fields_on_update = []
22
+ user_id_assign_fields_on_delete = []
23
+ deleted_at_field = None
24
+
25
+ def __init__(self, **kwargs):
26
+ # Check user_id_assign_fields_on_create
27
+ assert isinstance(self.user_id_assign_fields_on_create, list), (
28
+ 'Expected iterable user_id_assign_fields_on_create field'
29
+ )
30
+
31
+ # Check user_id_assign_fields_on_update
32
+ assert isinstance(self.user_id_assign_fields_on_update, list), (
33
+ 'Expected iterable user_id_assign_fields_on_update field'
34
+ )
35
+
36
+ # Check user_id_assign_fields_on_delete
37
+ assert isinstance(self.user_id_assign_fields_on_delete, list), (
38
+ 'Expected iterable user_id_assign_fields_on_delete field'
39
+ )
40
+
41
+ # Check deleted_at_field
42
+ assert isinstance(self.deleted_at_field, (str, type(None))), (
43
+ 'Expected str | None deleted_at_field field'
44
+ )
45
+ super().__init__(**kwargs)
46
+ self.request: BaseRequest | None = None
47
+
48
+ def perform_create(self, serializer):
49
+ kws = {}
50
+ for attr in getattr(self, 'user_id_assign_fields_on_create', []):
51
+ kws[attr] = self.request.user.id
52
+ serializer.save(**kws)
53
+
54
+ def perform_update(self, serializer):
55
+ kws = {}
56
+ for attr in getattr(self, 'user_id_assign_fields_on_update', []):
57
+ kws[attr] = self.request.user.id
58
+ serializer.save(**kws)
59
+
60
+ def perform_destroy(self, instance):
61
+ for attr in getattr(self, 'user_id_assign_fields_on_delete', []):
62
+ setattr(instance, attr, self.request.user.id)
63
+ if self.deleted_at_field and hasattr(instance, self.deleted_at_field):
64
+ setattr(instance, self.deleted_at_field, timezone.now())
65
+ instance.save()
66
+
67
+ def update_lang(self):
68
+ activate(self.get_language())
69
+
70
+ def get_language(self):
71
+ if lang_from_header := self.request.headers.get('accept-language'):
72
+ if lang_from_header and lang_from_header not in AVAILABLE_LANGUAGES:
73
+ lang_from_header = 'ru'
74
+
75
+ return lang_from_header or get_language()
utg_base/api/views.py ADDED
@@ -0,0 +1,13 @@
1
+
2
+ from django.db.models import Manager
3
+ from rest_framework import generics
4
+
5
+ from .base import BaseAPIView
6
+
7
+
8
+ class TranslatedListView(generics.ListAPIView, BaseAPIView):
9
+ manager: Manager
10
+
11
+ def get_queryset(self):
12
+ self.update_lang()
13
+ return self.manager.all()
@@ -0,0 +1 @@
1
+ from .jwt_user import JWTUser
@@ -0,0 +1,11 @@
1
+ from rest_framework_simplejwt.models import TokenUser
2
+ from rest_framework_simplejwt.tokens import Token
3
+
4
+
5
+ class JWTUser(TokenUser):
6
+
7
+ def __init__(self, token: "Token"):
8
+ super().__init__(token)
9
+ self.phone = self.token.get("phone")
10
+ self._groups = self.token.get("groups")
11
+ self.should_cache_requests = self.token.get("should_cache_requests")
File without changes
@@ -0,0 +1,3 @@
1
+ from django.contrib import admin
2
+
3
+ # Register your models here.
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class ReferencesApiConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ name = 'utg_base.references_api'
File without changes
@@ -0,0 +1,3 @@
1
+ from django.db import models
2
+
3
+ # Create your models here.
@@ -0,0 +1,13 @@
1
+ from .utils import get_model_classes, create_view_set, get_basename, get_url_prefix
2
+ from ..api.routers import OptionalSlashRouter
3
+
4
+ router = OptionalSlashRouter()
5
+
6
+ for model_class in get_model_classes():
7
+ router.register(
8
+ prefix=get_url_prefix(model_class),
9
+ viewset=create_view_set(model_class),
10
+ basename=get_basename(model_class)
11
+ )
12
+
13
+ urlpatterns = router.urls
@@ -0,0 +1,181 @@
1
+ import importlib
2
+ from urllib.parse import urlparse
3
+
4
+ import inflect
5
+ from django.conf import settings
6
+ from django.db.models import Model, CharField
7
+ from drf_spectacular.utils import extend_schema, extend_schema_serializer
8
+ from rest_framework import filters
9
+ from rest_framework import viewsets, serializers
10
+
11
+ from ..api.views import TranslatedListView
12
+
13
+ serializer_classes = {}
14
+
15
+
16
+ def find_model(model_name: str):
17
+ for model_class in get_model_classes():
18
+ if get_model_class_name(model_class).lower() == model_name.lower():
19
+ return model_class
20
+
21
+
22
+ def get_api_meta_property(model: Model, property_name: str):
23
+ if hasattr(model, 'ApiMeta'):
24
+ if hasattr(model.ApiMeta, property_name):
25
+ return getattr(model.ApiMeta, property_name)
26
+
27
+
28
+ def get_model_class_name(model: Model):
29
+ return model._meta.object_name
30
+
31
+
32
+ def get_model_fields_list(model: Model):
33
+ return model._meta.get_fields()
34
+
35
+
36
+ def get_view_set_name(model: Model):
37
+ return get_model_class_name(model) + 'ViewSet'
38
+
39
+
40
+ def get_basename(model: Model):
41
+ return 'admin-' + get_plural_form(get_model_class_name(model).lower())
42
+
43
+
44
+ def camel_to_snake(name: str):
45
+ return ''.join(map(lambda c: '-' + c.lower() if c.isupper() else c, name)).removeprefix('-')
46
+
47
+
48
+ def get_url_prefix(model: Model):
49
+ return 'admin/' + get_plural_form(camel_to_snake(get_model_class_name(model))).replace(' ', '-')
50
+
51
+
52
+ def get_plural_form(word: str):
53
+ p = inflect.engine()
54
+ return p.plural(word)
55
+
56
+
57
+ def get_serializer_name(model: Model):
58
+ return get_model_class_name(model) + 'Serializer'
59
+
60
+
61
+ def get_ordering(model: Model):
62
+ if model._meta.ordering:
63
+ return model._meta.ordering
64
+ if hasattr(model, 'created_at'):
65
+ return ('-created_at',)
66
+ if hasattr(model, 'id'):
67
+ return ('id',)
68
+ return ()
69
+
70
+
71
+ def get_model_classes():
72
+ model_classes = []
73
+ for model_import_path in settings.REFERENCE_API_MODELS:
74
+ module_name, class_name = model_import_path.rsplit('.', 1)
75
+ module = importlib.import_module(module_name)
76
+ model_classes.append(getattr(module, class_name))
77
+
78
+ return model_classes
79
+
80
+
81
+ def create_view_set(model: Model):
82
+ fields_list = get_model_fields_list(model)
83
+
84
+ @extend_schema(tags=[get_url_prefix(model)])
85
+ class ViewSet(viewsets.ModelViewSet, TranslatedListView):
86
+ http_method_names = get_api_meta_property(model, 'http_method_names') or ['get', 'patch']
87
+ manager = model.objects.order_by(*get_ordering(model))
88
+ filter_backends = [filters.SearchFilter]
89
+ search_fields = (get_api_meta_property(model, 'search_fields') or
90
+ [field.name for field in fields_list if isinstance(field, CharField)])
91
+
92
+ def get_serializer_class(self):
93
+ if self.action == 'create':
94
+ return create_serializer_for_create(model)
95
+
96
+ if self.action == 'partial_update':
97
+ return create_serializer_for_partial_update(model)
98
+
99
+ return create_serializer(model)
100
+
101
+ return ViewSet
102
+
103
+
104
+ def create_serializer_for_create(model: Model):
105
+ @extend_schema_serializer(component_name='Admin' + get_serializer_name(model))
106
+ class Serializer(serializers.ModelSerializer):
107
+ class Meta:
108
+ fields = '__all__'
109
+
110
+ Serializer.Meta.model = model
111
+ return Serializer
112
+
113
+
114
+ def create_serializer_for_partial_update(model: Model):
115
+ fields_list = get_model_fields_list(model)
116
+
117
+ @extend_schema_serializer(component_name='Admin' + get_serializer_name(model))
118
+ class Serializer(serializers.ModelSerializer):
119
+ class Meta:
120
+ fields = '__all__'
121
+ read_only_fields = list(
122
+ {
123
+ 'created_at',
124
+ 'updated_at',
125
+ 'deleted_at',
126
+ 'created_by',
127
+ 'updated_by',
128
+ 'deleted_by',
129
+ 'code'
130
+ } & set([field.name for field in fields_list])
131
+ )
132
+
133
+ Serializer.Meta.model = model
134
+ return Serializer
135
+
136
+
137
+ def create_serializer(model: Model, depth=0):
138
+ if get_model_class_name(model) in serializer_classes:
139
+ return serializer_classes[get_model_class_name(model)]
140
+
141
+ fields_list = get_model_fields_list(model)
142
+
143
+ class ImageNameField(serializers.ImageField):
144
+ def to_representation(self, value):
145
+ if not value:
146
+ return None
147
+ parsed_url = urlparse(value.url)
148
+ return f'{parsed_url.path}?{parsed_url.query}'
149
+
150
+ @extend_schema_serializer(component_name='Admin' + get_serializer_name(model))
151
+ class Serializer(serializers.ModelSerializer):
152
+ def __init__(self, *args, **kwargs):
153
+ super().__init__(*args, **kwargs)
154
+ nonlocal model, fields_list
155
+ for field in fields_list:
156
+ if field.__class__.__name__.endswith('ImageField') or field.__class__.__name__.endswith('FileField'):
157
+ self.fields[field.name] = ImageNameField()
158
+
159
+ if field.__class__.__name__.endswith('ForeignKey'):
160
+ model_name = field.name
161
+ _model = find_model(model_name)
162
+ if _model is not None and depth == 0:
163
+ self.fields[model_name] = create_serializer(_model, depth + 1)()
164
+
165
+ class Meta:
166
+ fields = '__all__'
167
+ read_only_fields = list(
168
+ {
169
+ 'created_at',
170
+ 'updated_at',
171
+ 'deleted_at',
172
+ 'created_by',
173
+ 'updated_by',
174
+ 'deleted_by',
175
+ 'code'
176
+ } & set([field.name for field in fields_list])
177
+ )
178
+
179
+ Serializer.Meta.model = model
180
+ serializer_classes[get_model_class_name(model)] = Serializer
181
+ return Serializer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utg-base
3
- Version: 1.0.1
3
+ Version: 1.1.1
4
4
  Summary: UTG Base Package
5
5
  Author: Rovshen
6
6
  Author-email: rovshenashirov1619@gmail.com
@@ -0,0 +1,22 @@
1
+ utg_base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ utg_base/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ utg_base/api/base.py,sha256=gbL3TtJKqdstpfbycvHorqpWaVRI87C1zix_MrJn0Cc,2560
4
+ utg_base/api/pagination.py,sha256=zzIUwW3iF5G_11gFsno9y1DmgFiULQIWRHUj0LKhYfE,854
5
+ utg_base/api/routers.py,sha256=lU54MVN2BF_q1AWp9EdXkG3m_ivYRtvbNGXFIRKz7u0,177
6
+ utg_base/api/views.py,sha256=yYCEJRouFA71cI2Ubc1A736oLg9NGWyTIVnD-Q85k6w,279
7
+ utg_base/authentications/__init__.py,sha256=a6twO_bBf8FAHYl7PXawfR2UbBwwdueG1uS_dbq2g_I,109
8
+ utg_base/authentications/microservice_authentication.py,sha256=6aAncxIpA4FcyRegd7QqRYvW5Wn8FxyPU0nQqCVuEs4,976
9
+ utg_base/authentications/models.py,sha256=JQonSdXeSeoF003QlmPvH58nWmVJRKlWWjW_ySqXaYg,2496
10
+ utg_base/logging.py,sha256=MNPUQ9Hdg-0YTFlJBOwhajqKz-8Y5P4POkqRNIU9ga8,5078
11
+ utg_base/models/__init__.py,sha256=ylLp_n2WJqvmuG-4X8yv119Ikcx-OFSYHUcnggUeQeo,30
12
+ utg_base/models/jwt_user.py,sha256=6TQ5wB_OZBtGhRL-2MonBGZm0n0Y86s4BRTxiRlUJOk,375
13
+ utg_base/references_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ utg_base/references_api/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
15
+ utg_base/references_api/apps.py,sha256=thAGmO-ZT-OD9dHHBSQRL_RRt-Es_jt-mEmHgVTpERs,168
16
+ utg_base/references_api/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ utg_base/references_api/models.py,sha256=Vjc0p2XbAPgE6HyTF6vll98A4eDhA5AvaQqsc4kQ9AQ,57
18
+ utg_base/references_api/urls.py,sha256=WkLACQ8GfK5pJkvt8FuYdOxcqSZHj7pYRw51M9WluGw,390
19
+ utg_base/references_api/utils.py,sha256=VzQMnGeWC0I-6BkK_9Lo9eSH6wd4C766oQ84n7SBWyc,5818
20
+ utg_base-1.1.1.dist-info/METADATA,sha256=E1h80NzMbq-KXo5kWFlv-sGVZeKqlihucjvSJt_w2YM,660
21
+ utg_base-1.1.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
22
+ utg_base-1.1.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- utg_base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- utg_base/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- utg_base/api/pagination.py,sha256=zzIUwW3iF5G_11gFsno9y1DmgFiULQIWRHUj0LKhYfE,854
4
- utg_base/api/routers.py,sha256=lU54MVN2BF_q1AWp9EdXkG3m_ivYRtvbNGXFIRKz7u0,177
5
- utg_base/authentications/__init__.py,sha256=a6twO_bBf8FAHYl7PXawfR2UbBwwdueG1uS_dbq2g_I,109
6
- utg_base/authentications/microservice_authentication.py,sha256=6aAncxIpA4FcyRegd7QqRYvW5Wn8FxyPU0nQqCVuEs4,976
7
- utg_base/authentications/models.py,sha256=JQonSdXeSeoF003QlmPvH58nWmVJRKlWWjW_ySqXaYg,2496
8
- utg_base/logging.py,sha256=MNPUQ9Hdg-0YTFlJBOwhajqKz-8Y5P4POkqRNIU9ga8,5078
9
- utg_base-1.0.1.dist-info/METADATA,sha256=9pWfSfgN80dgSZiLaVflI9ttLoAwSxxhpu8vUHqsCSY,660
10
- utg_base-1.0.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
11
- utg_base-1.0.1.dist-info/RECORD,,