From 777a7f6de5117e8b2425c20165d0535e0340fd61 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 6 May 2021 12:06:31 +0200 Subject: [PATCH 1/5] Bump to Django 2.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4a828a28..4bb18cb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==2.0 +Django==2.1 django-fluent-dashboard==1.0.1 django-admin-tools==0.9.1 django-extensions==2.1.1 From d5fce3b6e26af517bfad23c349e8d3a6b0dc67c1 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 6 May 2021 12:07:17 +0200 Subject: [PATCH 2/5] Replace string_concat() with format_lazy() On Django 1.11 django.utils.translation.string_concat() is deprecated in favor of django.utils.text.format_lazy() and it has been removed on Django 2.1 --- orchestra/contrib/services/helpers.py | 27 ++++++++++++++++++++++----- orchestra/contrib/services/models.py | 13 +++++++------ orchestra/core/__init__.py | 17 +++++++++-------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/orchestra/contrib/services/helpers.py b/orchestra/contrib/services/helpers.py index 3146dbb3..2e359d2d 100644 --- a/orchestra/contrib/services/helpers.py +++ b/orchestra/contrib/services/helpers.py @@ -1,3 +1,7 @@ +from django.utils.text import format_lazy +from django.utils.translation import ugettext_lazy + + def get_chunks(porders, ini, end, ix=0): if ix >= len(porders): return [[ini, end, []]] @@ -41,10 +45,10 @@ class Interval(object): self.ini = ini self.end = end self.order = order - + def __len__(self): return max((self.end-self.ini).days, 0) - + def __sub__(self, other): remaining = [] if self.ini < other.ini: @@ -52,13 +56,13 @@ class Interval(object): if self.end > other.end: remaining.append(Interval(max(self.ini,other.end), self.end, self.order)) return remaining - + def __repr__(self): return "".format( ini=self.ini.strftime('%Y-%-m-%-d'), end=self.end.strftime('%Y-%-m-%-d') ) - + def intersect(self, other, remaining_self=None, remaining_other=None): if remaining_self is not None: remaining_self += (self - other) @@ -69,7 +73,7 @@ class Interval(object): return result else: return None - + def intersect_set(self, others, remaining_self=None, remaining_other=None): intersections = [] for interval in others: @@ -130,3 +134,16 @@ def compensate(order, compensations): for __, compensation in ordered_intersections: remaining_compensations.append(compensation) return remaining_compensations, applied_compensations + + +def get_rate_methods_help_text(rate_class): + method_help_texts = [ + format_lazy('{}' * 4, *['
  ', method.verbose_name, ': ', method.help_text]) + for method in rate_class.get_methods().values() + ] + prefix = ugettext_lazy("Algorithm used to interprete the rating table.") + help_text_items = [prefix] + method_help_texts + return format_lazy( + '{}' * len(help_text_items), + *help_text_items + ) diff --git a/orchestra/contrib/services/models.py b/orchestra/contrib/services/models.py index 961c5c6b..5dffcb48 100644 --- a/orchestra/contrib/services/models.py +++ b/orchestra/contrib/services/models.py @@ -1,18 +1,20 @@ import calendar import decimal +from orchestra.contrib.services import helpers from django.contrib.contenttypes.models import ContentType from django.db import models from django.apps import apps from django.utils.functional import cached_property from django.utils.module_loading import autodiscover_modules -from django.utils.translation import string_concat, ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _ from orchestra.core import caches, validators from orchestra.utils.python import import_class from . import settings from .handlers import ServiceHandler +from .helpers import get_rate_methods_help_text autodiscover_modules('handlers') @@ -145,13 +147,12 @@ class Service(models.Model): (ANUAL, _("Anual data")), ), default=BILLING_PERIOD) - rate_algorithm = models.CharField(_("rate algorithm"), max_length=64, + rate_algorithm = models.CharField( + _("rate algorithm"), max_length=64, choices=rate_class.get_choices(), default=rate_class.get_default(), - help_text=string_concat(_("Algorithm used to interprete the rating table."), *[ - string_concat('
  ', method.verbose_name, ': ', method.help_text) - for name, method in rate_class.get_methods().items() - ])) + help_text=get_rate_methods_help_text(rate_class), + ) on_cancel = models.CharField(_("on cancel"), max_length=16, help_text=_("Defines the cancellation behaviour of this service."), choices=( diff --git a/orchestra/core/__init__.py b/orchestra/core/__init__.py index db2cd978..9d540eee 100644 --- a/orchestra/core/__init__.py +++ b/orchestra/core/__init__.py @@ -1,4 +1,4 @@ -from django.utils.translation import string_concat +from django.utils.text import format_lazy from ..utils.python import AttrDict @@ -7,16 +7,16 @@ class Register(object): def __init__(self, verbose_name=None): self._registry = {} self.verbose_name = verbose_name - + def __contains__(self, key): return key in self._registry - + def __getitem__(self, key): return self._registry[key] - + def __iter__(self): return iter(self._registry.values()) - + def register(self, model, **kwargs): if model in self._registry: raise KeyError("%s already registered" % model) @@ -31,14 +31,15 @@ class Register(object): } defaults.update(kwargs) self._registry[model] = AttrDict(**defaults) - + def register_view(self, view_name, **kwargs): if 'verbose_name' not in kwargs: raise KeyError("%s verbose_name is required for views" % view_name) if 'verbose_name_plural' not in kwargs: - kwargs['verbose_name_plural'] = string_concat(kwargs['verbose_name'], 's') + kwargs['verbose_name_plural'] = format_lazy('{}' * 2, *[kwargs['verbose_name'], 's']) + self.register(view_name, **kwargs) - + def get(self, *args): if args: return self._registry[args[0]] From 085b8f85bdd99cbfaebe186f36034b15e5499453 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 6 May 2021 12:58:54 +0200 Subject: [PATCH 3/5] Bump to django-filter 2.2.0 Compatible with Django 2.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4bb18cb5..cd504831 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ Markdown==2.4 djangorestframework==3.9.3 ecdsa==0.11 Pygments==1.6 -django-filter==1.1 +django-filter==2.2.0 jsonfield==0.9.22 python-dateutil==2.2 https://github.com/glic3rinu/passlib/archive/master.zip From ffd08459c41566ad4beb777e14c58fdb4fcc4e7b Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 6 May 2021 13:07:56 +0200 Subject: [PATCH 4/5] Bump to django-rest-framework 3.10.3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cd504831..7e35c884 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ celery==3.1.23 kombu==3.0.35 billiard==3.3.0.23 Markdown==2.4 -djangorestframework==3.9.3 +djangorestframework==3.10.3 ecdsa==0.11 Pygments==1.6 django-filter==2.2.0 From f7627926cbfdee58be737828a7d5a01acabdd541 Mon Sep 17 00:00:00 2001 From: Santiago Lamora Date: Thu, 6 May 2021 13:08:21 +0200 Subject: [PATCH 5/5] Replace detail_route with action decorator DRF 3.10.0 deprecates the detail_route decorator in favor of action --- orchestra/api/actions.py | 4 ++-- orchestra/contrib/bills/api.py | 6 +++--- orchestra/contrib/domains/api.py | 10 +++++----- orchestra/contrib/issues/api.py | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/orchestra/api/actions.py b/orchestra/api/actions.py index 8e2c3531..d1b90264 100644 --- a/orchestra/api/actions.py +++ b/orchestra/api/actions.py @@ -1,12 +1,12 @@ from rest_framework import status -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.response import Response from .serializers import SetPasswordSerializer class SetPasswordApiMixin(object): - @detail_route(methods=['post'], serializer_class=SetPasswordSerializer) + @action(detail=True, methods=['post'], serializer_class=SetPasswordSerializer) def set_password(self, request, pk): obj = self.get_object() data = request.data diff --git a/orchestra/contrib/bills/api.py b/orchestra/contrib/bills/api.py index 6bb16a85..7d050b4c 100644 --- a/orchestra/contrib/bills/api.py +++ b/orchestra/contrib/bills/api.py @@ -1,6 +1,6 @@ from django.http import HttpResponse from rest_framework import viewsets -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from orchestra.api import router, LogApiMixin from orchestra.contrib.accounts.api import AccountApiMixin @@ -14,8 +14,8 @@ from .serializers import BillSerializer class BillViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet): queryset = Bill.objects.all() serializer_class = BillSerializer - - @detail_route(methods=['get']) + + @action(detail=True, methods=['get']) def document(self, request, pk): bill = self.get_object() content_type = request.META.get('HTTP_ACCEPT') diff --git a/orchestra/contrib/domains/api.py b/orchestra/contrib/domains/api.py index 38ab2e40..27a25453 100644 --- a/orchestra/contrib/domains/api.py +++ b/orchestra/contrib/domains/api.py @@ -1,5 +1,5 @@ from rest_framework import viewsets -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.response import Response from orchestra.api import router @@ -14,18 +14,18 @@ class DomainViewSet(AccountApiMixin, viewsets.ModelViewSet): serializer_class = DomainSerializer filter_fields = ('name',) queryset = Domain.objects.all() - + def get_queryset(self): qs = super(DomainViewSet, self).get_queryset() return qs.prefetch_related('records') - - @detail_route() + + @action(detail=True) def view_zone(self, request, pk=None): domain = self.get_object() return Response({ 'zone': domain.render_zone() }) - + def options(self, request): metadata = super(DomainViewSet, self).options(request) names = ['DOMAINS_DEFAULT_A', 'DOMAINS_DEFAULT_MX', 'DOMAINS_DEFAULT_NS'] diff --git a/orchestra/contrib/issues/api.py b/orchestra/contrib/issues/api.py index 15974a11..bbc5d5ee 100644 --- a/orchestra/contrib/issues/api.py +++ b/orchestra/contrib/issues/api.py @@ -1,5 +1,5 @@ from rest_framework import viewsets, mixins -from rest_framework.decorators import detail_route +from rest_framework.decorators import action from rest_framework.response import Response from orchestra.api import router, LogApiMixin @@ -12,19 +12,19 @@ from .serializers import TicketSerializer, QueueSerializer class TicketViewSet(LogApiMixin, viewsets.ModelViewSet): queryset = Ticket.objects.all() serializer_class = TicketSerializer - - @detail_route() + + @action(detail=True) def mark_as_read(self, request, pk=None): ticket = self.get_object() ticket.mark_as_read_by(request.user) return Response({'status': 'Ticket marked as read'}) - - @detail_route() + + @action(detail=True) def mark_as_unread(self, request, pk=None): ticket = self.get_object() ticket.mark_as_unread_by(request.user) return Response({'status': 'Ticket marked as unread'}) - + def get_queryset(self): qs = super(TicketViewSet, self).get_queryset() qs = qs.select_related('creator', 'queue')