utg-base 1.5.1__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 (53) hide show
  1. utg_base-1.5.1/PKG-INFO +24 -0
  2. utg_base-1.5.1/README.md +1 -0
  3. utg_base-1.5.1/pyproject.toml +30 -0
  4. utg_base-1.5.1/src/utg_base/__init__.py +0 -0
  5. utg_base-1.5.1/src/utg_base/api/__init__.py +0 -0
  6. utg_base-1.5.1/src/utg_base/api/base.py +73 -0
  7. utg_base-1.5.1/src/utg_base/api/pagination.py +33 -0
  8. utg_base-1.5.1/src/utg_base/api/permissions.py +11 -0
  9. utg_base-1.5.1/src/utg_base/api/routers.py +7 -0
  10. utg_base-1.5.1/src/utg_base/api/serializers.py +39 -0
  11. utg_base-1.5.1/src/utg_base/api/views.py +13 -0
  12. utg_base-1.5.1/src/utg_base/authentications/__init__.py +2 -0
  13. utg_base-1.5.1/src/utg_base/authentications/microservice_authentication.py +29 -0
  14. utg_base-1.5.1/src/utg_base/authentications/models.py +92 -0
  15. utg_base-1.5.1/src/utg_base/celery/__init__.py +0 -0
  16. utg_base-1.5.1/src/utg_base/celery/apps.py +6 -0
  17. utg_base-1.5.1/src/utg_base/celery/filters/__init__.py +1 -0
  18. utg_base-1.5.1/src/utg_base/celery/filters/task_result.py +5 -0
  19. utg_base-1.5.1/src/utg_base/celery/management/__init__.py +0 -0
  20. utg_base-1.5.1/src/utg_base/celery/management/commands/__init__.py +0 -0
  21. utg_base-1.5.1/src/utg_base/celery/management/commands/migrate_tasks.py +45 -0
  22. utg_base-1.5.1/src/utg_base/celery/serializers/__init__.py +2 -0
  23. utg_base-1.5.1/src/utg_base/celery/serializers/periodic_task.py +38 -0
  24. utg_base-1.5.1/src/utg_base/celery/serializers/task_result.py +8 -0
  25. utg_base-1.5.1/src/utg_base/celery/urls.py +14 -0
  26. utg_base-1.5.1/src/utg_base/celery/views/__init__.py +2 -0
  27. utg_base-1.5.1/src/utg_base/celery/views/periodic_task.py +56 -0
  28. utg_base-1.5.1/src/utg_base/celery/views/task_result.py +18 -0
  29. utg_base-1.5.1/src/utg_base/constants/__init__.py +1 -0
  30. utg_base-1.5.1/src/utg_base/constants/available_languages.py +5 -0
  31. utg_base-1.5.1/src/utg_base/env.py +27 -0
  32. utg_base-1.5.1/src/utg_base/logging.py +125 -0
  33. utg_base-1.5.1/src/utg_base/middleware/__init__.py +0 -0
  34. utg_base-1.5.1/src/utg_base/middleware/locale.py +13 -0
  35. utg_base-1.5.1/src/utg_base/models/__init__.py +2 -0
  36. utg_base-1.5.1/src/utg_base/models/jwt_user.py +11 -0
  37. utg_base-1.5.1/src/utg_base/models/timestamp.py +9 -0
  38. utg_base-1.5.1/src/utg_base/references_api/__init__.py +0 -0
  39. utg_base-1.5.1/src/utg_base/references_api/admin.py +3 -0
  40. utg_base-1.5.1/src/utg_base/references_api/apps.py +6 -0
  41. utg_base-1.5.1/src/utg_base/references_api/migrations/__init__.py +0 -0
  42. utg_base-1.5.1/src/utg_base/references_api/models.py +3 -0
  43. utg_base-1.5.1/src/utg_base/references_api/urls.py +13 -0
  44. utg_base-1.5.1/src/utg_base/references_api/utils.py +181 -0
  45. utg_base-1.5.1/src/utg_base/services/__init__.py +1 -0
  46. utg_base-1.5.1/src/utg_base/services/base_api.py +128 -0
  47. utg_base-1.5.1/src/utg_base/utils/__init__.py +2 -0
  48. utg_base-1.5.1/src/utg_base/utils/data.py +106 -0
  49. utg_base-1.5.1/src/utg_base/utils/date.py +126 -0
  50. utg_base-1.5.1/src/utg_base/utils/dict_util.py +20 -0
  51. utg_base-1.5.1/src/utg_base/utils/response_processors.py +31 -0
  52. utg_base-1.5.1/src/utg_base/utils/sql.py +25 -0
  53. utg_base-1.5.1/src/utg_base/utils/translation.py +18 -0
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.1
2
+ Name: utg-base
3
+ Version: 1.5.1
4
+ Summary: UTG Base Package
5
+ Author: Rovshen
6
+ Author-email: rovshenashirov1619@gmail.com
7
+ Requires-Python: >=3.14,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Requires-Dist: celery (>=5.5.3,<6.0.0)
10
+ Requires-Dist: croniter (>=2.0.3,<3.0.0)
11
+ Requires-Dist: django (>=5.2.7,<6.0.0)
12
+ Requires-Dist: django-celery-beat (>=2.8.1,<3.0.0)
13
+ Requires-Dist: django-celery-results (>=2.6.0,<3.0.0)
14
+ Requires-Dist: django-filter (>=23.5,<24.0)
15
+ Requires-Dist: djangorestframework (>=3.16.1,<4.0.0)
16
+ Requires-Dist: djangorestframework-simplejwt[crypto] (>=5.5.1,<6.0.0)
17
+ Requires-Dist: dotenv (>=0.9.9,<0.10.0)
18
+ Requires-Dist: drf-spectacular[sidecar] (>=0.29.0,<0.30.0)
19
+ Requires-Dist: hvac (>=2.4.0,<3.0.0)
20
+ Requires-Dist: inflect (>=7.2.1,<8.0.0)
21
+ Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
22
+ Description-Content-Type: text/markdown
23
+
24
+ # UzTransGas Base
@@ -0,0 +1 @@
1
+ # UzTransGas Base
@@ -0,0 +1,30 @@
1
+ [tool.poetry]
2
+ name = "utg-base"
3
+ version = "1.5.1"
4
+ description = "UTG Base Package"
5
+ authors = ["Rovshen <rovshenashirov1619@gmail.com>"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = "^3.14"
10
+ djangorestframework = "^3.16.1"
11
+ djangorestframework-simplejwt = {extras = ["crypto"], version = "^5.5.1"}
12
+ django = "^5.2.7"
13
+ django-filter = "^23.5"
14
+ croniter = "^2.0.3"
15
+ drf-spectacular = {extras = ["sidecar"], version = "^0.29.0"}
16
+ openpyxl = "^3.1.2"
17
+ inflect = "^7.2.1"
18
+ celery = "^5.5.3"
19
+ django-celery-beat = "^2.8.1"
20
+ django-celery-results = "^2.6.0"
21
+ hvac = "^2.4.0"
22
+ dotenv = "^0.9.9"
23
+
24
+ [[tool.poetry.source]]
25
+ name = "PyPI"
26
+ priority = "primary"
27
+
28
+ [build-system]
29
+ requires = ["poetry-core"]
30
+ build-backend = "poetry.core.masonry.api"
File without changes
File without changes
@@ -0,0 +1,73 @@
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 utg_base.constants import AVAILABLE_LANGUAGES
7
+ from utg_base.models import JWTUser
8
+
9
+
10
+ class BaseRequest(Request):
11
+
12
+ @property
13
+ def user(self) -> JWTUser:
14
+ return super(BaseRequest, self).user()
15
+
16
+
17
+ class BaseAPIView(APIView):
18
+ user_id_assign_fields_on_create = []
19
+ user_id_assign_fields_on_update = []
20
+ user_id_assign_fields_on_delete = []
21
+ deleted_at_field = None
22
+
23
+ def __init__(self, **kwargs):
24
+ # Check user_id_assign_fields_on_create
25
+ assert isinstance(self.user_id_assign_fields_on_create, list), (
26
+ 'Expected iterable user_id_assign_fields_on_create field'
27
+ )
28
+
29
+ # Check user_id_assign_fields_on_update
30
+ assert isinstance(self.user_id_assign_fields_on_update, list), (
31
+ 'Expected iterable user_id_assign_fields_on_update field'
32
+ )
33
+
34
+ # Check user_id_assign_fields_on_delete
35
+ assert isinstance(self.user_id_assign_fields_on_delete, list), (
36
+ 'Expected iterable user_id_assign_fields_on_delete field'
37
+ )
38
+
39
+ # Check deleted_at_field
40
+ assert isinstance(self.deleted_at_field, (str, type(None))), (
41
+ 'Expected str | None deleted_at_field field'
42
+ )
43
+ super().__init__(**kwargs)
44
+ self.request: BaseRequest | None = None
45
+
46
+ def perform_create(self, serializer):
47
+ kws = {}
48
+ for attr in getattr(self, 'user_id_assign_fields_on_create', []):
49
+ kws[attr] = self.request.user.id
50
+ serializer.save(**kws)
51
+
52
+ def perform_update(self, serializer):
53
+ kws = {}
54
+ for attr in getattr(self, 'user_id_assign_fields_on_update', []):
55
+ kws[attr] = self.request.user.id
56
+ serializer.save(**kws)
57
+
58
+ def perform_destroy(self, instance):
59
+ for attr in getattr(self, 'user_id_assign_fields_on_delete', []):
60
+ setattr(instance, attr, self.request.user.id)
61
+ if self.deleted_at_field and hasattr(instance, self.deleted_at_field):
62
+ setattr(instance, self.deleted_at_field, timezone.now())
63
+ instance.save()
64
+
65
+ def update_lang(self):
66
+ activate(self.get_language())
67
+
68
+ def get_language(self):
69
+ if lang_from_header := self.request.headers.get('accept-language'):
70
+ if lang_from_header and lang_from_header not in AVAILABLE_LANGUAGES:
71
+ lang_from_header = 'ru'
72
+
73
+ return lang_from_header or get_language()
@@ -0,0 +1,33 @@
1
+ from rest_framework.pagination import PageNumberPagination
2
+ from rest_framework.response import Response
3
+
4
+
5
+ class Pagination(PageNumberPagination):
6
+ page_size_query_param = 'page_size'
7
+ max_page_size = 200
8
+
9
+ def get_paginated_response(self, data):
10
+ return Response({
11
+ 'page': self.page.number,
12
+ 'pageSize': self.page.paginator.per_page,
13
+ 'count': self.page.end_index() - self.page.start_index() + 1,
14
+ 'total': self.page.paginator.count,
15
+ 'pagesCount': self.page.paginator.num_pages,
16
+ 'data': data,
17
+ })
18
+
19
+
20
+ class LargeResultsSetPagination(Pagination):
21
+ page_size = 50
22
+
23
+
24
+ class StandardResultsSetPagination(Pagination):
25
+ page_size = 20
26
+
27
+
28
+ class SmallResultsSetPagination(Pagination):
29
+ page_size = 10
30
+
31
+
32
+ class MiniResultsSetPagination(Pagination):
33
+ page_size = 5
@@ -0,0 +1,11 @@
1
+ from rest_framework.permissions import BasePermission
2
+
3
+
4
+ class IsSuperUser(BasePermission):
5
+ """
6
+ Custom permission to allow access only to superusers.
7
+ """
8
+
9
+ def has_permission(self, request, view):
10
+ # Check if the requesting user is a superuser
11
+ return request.user and request.user.is_superuser
@@ -0,0 +1,7 @@
1
+ from rest_framework.routers import SimpleRouter
2
+
3
+
4
+ class OptionalSlashRouter(SimpleRouter):
5
+ def __init__(self):
6
+ super().__init__()
7
+ self.trailing_slash = '/?'
@@ -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,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,2 @@
1
+ from .models import MicroserviceUser
2
+ from .microservice_authentication import MicroserviceUserAuthentication
@@ -0,0 +1,29 @@
1
+ from ipaddress import ip_address, ip_network
2
+
3
+ from rest_framework import authentication
4
+
5
+ from . import MicroserviceUser
6
+
7
+
8
+ class MicroserviceUserAuthentication(authentication.BaseAuthentication):
9
+ # List of local IP address ranges
10
+ local_ip_ranges = [
11
+ '127.0.0.0/8', # Loopback addresses
12
+ '10.0.0.0/8', # Private network addresses
13
+ '172.16.0.0/12', # Private network addresses
14
+ '192.168.0.0/16', # Private network addresses
15
+ # Add more local ranges if needed
16
+ ]
17
+
18
+ def authenticate(self, request):
19
+ client_ip = request.META.get('REMOTE_ADDR', None)
20
+ server_ip = request.META.get('SERVER_ADDR', None)
21
+
22
+ if any(ip_address(client_ip) in ip_network(ip_range, strict=False) for ip_range in self.local_ip_ranges):
23
+ return self.get_user(), None
24
+
25
+ if client_ip == server_ip:
26
+ return self.get_user(), None
27
+
28
+ def get_user(self) -> MicroserviceUser:
29
+ return MicroserviceUser()
@@ -0,0 +1,92 @@
1
+ from typing import List, Optional, Union
2
+
3
+ from django.contrib.auth import models as auth_models
4
+ from django.db.models.manager import EmptyManager
5
+ from django.utils.functional import cached_property
6
+
7
+
8
+ class MicroserviceUser:
9
+ is_active = True
10
+
11
+ _groups = EmptyManager(auth_models.Group)
12
+ _user_permissions = EmptyManager(auth_models.Permission)
13
+
14
+ def __str__(self) -> str:
15
+ return f"{self.id}"
16
+
17
+ @cached_property
18
+ def id(self) -> Union[int, str]:
19
+ return -1
20
+
21
+ @cached_property
22
+ def pk(self) -> Union[int, str]:
23
+ return self.id
24
+
25
+ @cached_property
26
+ def username(self) -> str:
27
+ return 'microservice'
28
+
29
+ @cached_property
30
+ def is_staff(self) -> bool:
31
+ return True
32
+
33
+ @cached_property
34
+ def is_superuser(self) -> bool:
35
+ return True
36
+
37
+ def __eq__(self, other: object) -> bool:
38
+ if not isinstance(other, MicroserviceUser):
39
+ return NotImplemented
40
+ return self.id == other.id
41
+
42
+ def __ne__(self, other: object) -> bool:
43
+ return not self.__eq__(other)
44
+
45
+ def __hash__(self) -> int:
46
+ return hash(self.id)
47
+
48
+ def save(self) -> None:
49
+ raise NotImplementedError("Microservice users have no DB representation")
50
+
51
+ def delete(self) -> None:
52
+ raise NotImplementedError("Microservice users have no DB representation")
53
+
54
+ def set_password(self, raw_password: str) -> None:
55
+ raise NotImplementedError("Microservice users have no DB representation")
56
+
57
+ def check_password(self, raw_password: str) -> None:
58
+ raise NotImplementedError("Microservice users have no DB representation")
59
+
60
+ @property
61
+ def groups(self) -> auth_models.Group:
62
+ return self._groups
63
+
64
+ @property
65
+ def user_permissions(self) -> auth_models.Permission:
66
+ return self._user_permissions
67
+
68
+ def get_group_permissions(self, obj: Optional[object] = None) -> set:
69
+ return set()
70
+
71
+ def get_all_permissions(self, obj: Optional[object] = None) -> set:
72
+ return set()
73
+
74
+ def has_perm(self, perm: str, obj: Optional[object] = None) -> bool:
75
+ return False
76
+
77
+ def has_perms(self, perm_list: List[str], obj: Optional[object] = None) -> bool:
78
+ return False
79
+
80
+ def has_module_perms(self, module: str) -> bool:
81
+ return False
82
+
83
+ @property
84
+ def is_anonymous(self) -> bool:
85
+ return False
86
+
87
+ @property
88
+ def is_authenticated(self) -> bool:
89
+ return True
90
+
91
+ def get_username(self) -> str:
92
+ return self.username
File without changes
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class QApiConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "utg_base.celery"
@@ -0,0 +1 @@
1
+ from .task_result import TaskResultFilterSet
@@ -0,0 +1,5 @@
1
+ from django_filters import rest_framework as filters
2
+
3
+
4
+ class TaskResultFilterSet(filters.FilterSet):
5
+ status = filters.BooleanFilter()
@@ -0,0 +1,45 @@
1
+ import importlib
2
+ import json
3
+
4
+ from django.core.management.base import BaseCommand
5
+ from django_celery_beat.models import CrontabSchedule, PeriodicTask
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = "Migrate tasks"
10
+
11
+ def add_arguments(self, parser):
12
+ parser.add_argument('--app', type=str, help="An optional argument", required=True)
13
+
14
+ def handle(self, *args, **options):
15
+ try:
16
+ tasks = importlib.import_module(f"{options['app']}.tasks")
17
+ tasks = tasks.tasks
18
+ for task in tasks:
19
+ celery_task, _ = PeriodicTask.objects.update_or_create(
20
+ name=task['name'],
21
+ defaults={
22
+ 'crontab': self.get_crontab_schedule(task['crontab']),
23
+ 'task': task['task'].name,
24
+ 'args': json.dumps(task.get('args') or []),
25
+ 'kwargs': json.dumps(task.get('kwargs') or {}),
26
+ 'enabled': task.get('enabled', True),
27
+ }
28
+ )
29
+ self.stdout.write(
30
+ self.style.SUCCESS('Successfully migrated tasks')
31
+ )
32
+ except ModuleNotFoundError:
33
+ self.stdout.write(self.style.ERROR("Tasks module not found"))
34
+
35
+ @staticmethod
36
+ def get_crontab_schedule(crontab='* * * * *'):
37
+ minute, hour, day_of_month, month_of_year, day_of_week = crontab.split(' ')
38
+ cron, _ = CrontabSchedule.objects.get_or_create(
39
+ minute=minute,
40
+ hour=hour,
41
+ day_of_month=day_of_month,
42
+ month_of_year=month_of_year,
43
+ day_of_week=day_of_week,
44
+ )
45
+ return cron
@@ -0,0 +1,2 @@
1
+ from .task_result import TaskResultSerializer
2
+ from .periodic_task import PeriodicTaskSerializer
@@ -0,0 +1,38 @@
1
+ from django_celery_beat.models import PeriodicTask, CrontabSchedule
2
+ from django_celery_results.models import TaskResult
3
+ from rest_framework import serializers
4
+
5
+
6
+ from utg_base.celery.serializers import TaskResultSerializer
7
+
8
+
9
+ class CrontabScheduleSerializer(serializers.ModelSerializer):
10
+ timezone = serializers.SerializerMethodField()
11
+
12
+ class Meta:
13
+ model = CrontabSchedule
14
+ fields = '__all__'
15
+
16
+ def get_timezone(self, obj):
17
+ return str(obj.timezone) if obj.timezone else None
18
+
19
+
20
+ class PeriodicTaskSerializer(serializers.ModelSerializer):
21
+ crontab = CrontabScheduleSerializer(read_only=True)
22
+ task_result = serializers.SerializerMethodField(read_only=True)
23
+
24
+ class Meta:
25
+ model = PeriodicTask
26
+ fields = '__all__'
27
+ read_only_fields = ['name', 'task', 'args', 'kwargs', 'queue', 'exchange', 'routing_key', 'headers', 'priority',
28
+ 'expires', 'expire_seconds', 'one_off', 'start_time', 'interval', 'solar', 'clocked']
29
+
30
+ def get_task_result(self, obj):
31
+ latest_task_result = TaskResult.objects.filter(
32
+ periodic_task_name=obj.name
33
+ ).order_by('-date_created').first()
34
+
35
+ if not latest_task_result:
36
+ return None
37
+
38
+ return TaskResultSerializer(latest_task_result).data
@@ -0,0 +1,8 @@
1
+ from django_celery_results.models import TaskResult
2
+ from rest_framework import serializers
3
+
4
+
5
+ class TaskResultSerializer(serializers.ModelSerializer):
6
+ class Meta:
7
+ model = TaskResult
8
+ fields = '__all__'
@@ -0,0 +1,14 @@
1
+ from django.urls import path
2
+
3
+ from utg_base.api.routers import OptionalSlashRouter
4
+ from utg_base.celery.views import TaskResultViewSet, PeriodicTaskViewSet, PeriodicTaskRunNowView
5
+
6
+ router = OptionalSlashRouter()
7
+ router.register('task-results', TaskResultViewSet, basename='task-results')
8
+ router.register('periodic-tasks', PeriodicTaskViewSet, basename='periodic-tasks')
9
+
10
+ urlpatterns = [
11
+ path('periodic-tasks/<pk>/run-now/', PeriodicTaskRunNowView.as_view(), name='periodic-tasks-run-now'),
12
+ ]
13
+
14
+ urlpatterns += router.urls
@@ -0,0 +1,2 @@
1
+ from .periodic_task import PeriodicTaskViewSet, PeriodicTaskRunNowView
2
+ from .task_result import TaskResultViewSet
@@ -0,0 +1,56 @@
1
+ import json
2
+
3
+ from celery import current_app
4
+ from django_celery_beat.models import PeriodicTask
5
+ from drf_spectacular.utils import extend_schema
6
+ from rest_framework import viewsets, filters
7
+ from rest_framework.exceptions import NotFound, ValidationError
8
+ from rest_framework.permissions import IsAuthenticated
9
+ from rest_framework.response import Response
10
+ from rest_framework.views import APIView
11
+
12
+ from utg_base.api.permissions import IsSuperUser
13
+ from utg_base.celery.serializers import PeriodicTaskSerializer
14
+ from utg_base.utils.translation import translate as _
15
+
16
+
17
+ @extend_schema(tags=['admin/periodic-tasks'])
18
+ class PeriodicTaskViewSet(viewsets.ModelViewSet):
19
+ http_method_names = ['get', 'patch']
20
+ queryset = PeriodicTask.objects.all()
21
+ serializer_class = PeriodicTaskSerializer
22
+ permission_classes = [IsSuperUser]
23
+ filter_backends = [filters.SearchFilter]
24
+ search_fields = ['name']
25
+
26
+
27
+ @extend_schema(tags=['admin/periodic-tasks'])
28
+ class PeriodicTaskRunNowView(APIView):
29
+ permission_classes = [IsAuthenticated]
30
+
31
+ def patch(self, request, pk):
32
+ try:
33
+ periodic_task = PeriodicTask.objects.get(pk=pk)
34
+ except PeriodicTask.DoesNotExist:
35
+ raise NotFound(detail=_("Periodic task not found"))
36
+
37
+ if not periodic_task.enabled:
38
+ raise ValidationError(detail=_("Task is disabled"))
39
+
40
+ args = json.loads(periodic_task.args) if periodic_task.args else []
41
+ kwargs = json.loads(periodic_task.kwargs) if periodic_task.kwargs else {}
42
+
43
+ task = current_app.send_task(
44
+ periodic_task.task,
45
+ args=args,
46
+ kwargs=kwargs,
47
+ countdown=0,
48
+ headers = {'periodic_task_name': periodic_task.name}
49
+ )
50
+
51
+ return Response({
52
+ "detail": _("Task successfully triggered"),
53
+ "task_id": task.id,
54
+ "task_name": periodic_task.name,
55
+ "celery_task": periodic_task.task,
56
+ })
@@ -0,0 +1,18 @@
1
+ import django_filters
2
+ from django_celery_results.models import TaskResult
3
+ from drf_spectacular.utils import extend_schema
4
+ from rest_framework import viewsets, filters
5
+
6
+ from utg_base.celery.filters import TaskResultFilterSet
7
+ from utg_base.api.permissions import IsSuperUser
8
+ from utg_base.celery.serializers import TaskResultSerializer
9
+
10
+
11
+ @extend_schema(tags=['admin/task-results'])
12
+ class TaskResultViewSet(viewsets.ReadOnlyModelViewSet):
13
+ queryset = TaskResult.objects.all()
14
+ serializer_class = TaskResultSerializer
15
+ permission_classes = [IsSuperUser]
16
+ filterset_class = TaskResultFilterSet
17
+ filter_backends = [filters.SearchFilter, django_filters.rest_framework.DjangoFilterBackend]
18
+ search_fields = ['name']
@@ -0,0 +1 @@
1
+ from .available_languages import AVAILABLE_LANGUAGES
@@ -0,0 +1,5 @@
1
+ AVAILABLE_LANGUAGES = [
2
+ 'uz',
3
+ 'ru',
4
+ 'crl'
5
+ ]
@@ -0,0 +1,27 @@
1
+ import os
2
+
3
+ import dotenv
4
+ import hvac
5
+ from django.conf import settings
6
+
7
+ dotenv.load_dotenv(settings.BASE_DIR / '.env')
8
+
9
+ client = hvac.Client(
10
+ url=os.environ.get("VAULT_URL"),
11
+ token=os.environ.get("VAULT_TOKEN"),
12
+ verify=False
13
+ )
14
+ if not client.is_authenticated():
15
+ raise Exception("Vault authentication failed")
16
+
17
+ envs = client.secrets.kv.read_secret_version(
18
+ path=os.environ.get("VAULT_PATH"),
19
+ mount_point="utg-scada"
20
+ )['data']['data']
21
+
22
+
23
+ def env(key, default=None):
24
+ if key in os.environ:
25
+ return os.environ.get(key, default)
26
+ else:
27
+ return envs.get(key, default)