From 00cbf9287dd4d0b9975ad8f9e868e64786a4ee32 Mon Sep 17 00:00:00 2001 From: bashmak Date: Thu, 31 May 2018 13:17:46 +0300 Subject: [PATCH] refactoring: mydefs moved to djing.lib --- abonapp/migrations/0001_initial.py | 4 +- abonapp/models.py | 2 +- abonapp/pay_systems.py | 2 +- abonapp/views.py | 106 ++++++----- accounts_app/views.py | 17 +- agent/mod_mikrotik.py | 2 +- agent/structs.py | 34 ++-- clientsideapp/views.py | 2 +- devapp/migrations/0001_initial.py | 4 +- devapp/migrations/0002_auto_20180409_1318.py | 4 +- dialing_app/views.py | 2 +- djing/lib/__init__.py | 186 +++++++++++++++++++ djing/settings.py | 2 +- docs/dev.md | 2 +- mapapp/views.py | 2 +- periodic.py | 2 +- statistics/migrations/0001_initial.py | 5 +- statistics/models.py | 2 +- tariff_app/base_intr.py | 4 +- tariff_app/models.py | 2 +- tariff_app/views.py | 8 +- taskapp/handle.py | 2 +- taskapp/migrations/0001_initial.py | 2 +- taskapp/views.py | 7 +- 24 files changed, 294 insertions(+), 111 deletions(-) create mode 100644 djing/lib/__init__.py diff --git a/abonapp/migrations/0001_initial.py b/abonapp/migrations/0001_initial.py index 2877a67..e929df1 100644 --- a/abonapp/migrations/0001_initial.py +++ b/abonapp/migrations/0001_initial.py @@ -7,7 +7,7 @@ from django.conf import settings import django.core.validators from django.db import migrations, models import django.db.models.deletion -import mydefs +from djing import lib import re @@ -30,7 +30,7 @@ class Migration(migrations.Migration): models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='accounts_app.BaseAccount')), ('ballance', models.FloatField(default=0.0)), - ('ip_address', mydefs.MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4')), + ('ip_address', lib.MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4')), ('description', models.TextField(blank=True, null=True, verbose_name='Comment')), ('house', models.CharField(blank=True, max_length=12, null=True, verbose_name='House')), ('is_dynamic_ip', models.BooleanField(default=False)), diff --git a/abonapp/models.py b/abonapp/models.py index 6cf8e74..650f226 100644 --- a/abonapp/models.py +++ b/abonapp/models.py @@ -14,7 +14,7 @@ from django.utils.translation import ugettext_lazy as _, gettext from accounts_app.models import UserProfile, MyUserManager, BaseAccount from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError from group_app.models import Group -from mydefs import MyGenericIPAddressField, ip2int, LogicError +from djing.lib import ip2int, MyGenericIPAddressField, LogicError from djing import IP_ADDR_REGEX from tariff_app.models import Tariff, PeriodicPay from bitfield import BitField diff --git a/abonapp/pay_systems.py b/abonapp/pay_systems.py index 90b025d..6fe737e 100644 --- a/abonapp/pay_systems.py +++ b/abonapp/pay_systems.py @@ -1,6 +1,6 @@ from hashlib import md5 from django.utils import timezone -from mydefs import safe_int, safe_float +from djing.lib import safe_int, safe_float from .models import Abon, AllTimePayLog from django.db import DatabaseError from django.conf import settings diff --git a/abonapp/views.py b/abonapp/views.py index 5139d2a..5a36b85 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -20,7 +20,6 @@ from tariff_app.models import Tariff from agent import NasFailedResult, Transmitter, NasNetworkError from . import forms from . import models -import mydefs from devapp.models import Device, Port as DevPort from datetime import datetime, date, timedelta from taskapp.models import Task @@ -30,6 +29,7 @@ from group_app.models import Group from guardian.shortcuts import get_objects_for_user, assign_perm from guardian.decorators import permission_required_or_403 as permission_required from djing import ping +from djing import lib from djing.global_base_views import OrderingMixin, BaseListWithFiltering, SecureApiView PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) @@ -40,14 +40,14 @@ class BaseAbonListView(OrderingMixin, BaseListWithFiltering): http_method_names = ('get',) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') class PeoplesListView(BaseAbonListView): context_object_name = 'peoples' template_name = 'abonapp/peoples.html' def get_queryset(self): - street_id = mydefs.safe_int(self.request.GET.get('street')) - gid = mydefs.safe_int(self.kwargs.get('gid')) + street_id = lib.safe_int(self.request.GET.get('street')) + gid = lib.safe_int(self.kwargs.get('gid')) peoples_list = models.Abon.objects.all().select_related('group', 'street', 'current_tariff') if street_id > 0: peoples_list = peoples_list.filter(group__pk=gid, street=street_id) @@ -61,7 +61,7 @@ class PeoplesListView(BaseAbonListView): abon.stat_cache = StatCache.objects.get(ip=abon.ip_address) except StatCache.DoesNotExist: pass - except mydefs.LogicError as e: + except lib.LogicError as e: messages.warning(self.request, e) ordering = self.get_ordering() if ordering and isinstance(ordering, str): @@ -70,7 +70,7 @@ class PeoplesListView(BaseAbonListView): return peoples_list def get_context_data(self, **kwargs): - gid = mydefs.safe_int(self.kwargs.get('gid')) + gid = lib.safe_int(self.kwargs.get('gid')) if gid < 1: return HttpResponseBadRequest('group id is broken') group = get_object_or_404(Group, pk=gid) @@ -80,12 +80,12 @@ class PeoplesListView(BaseAbonListView): context = super(PeoplesListView, self).get_context_data(**kwargs) context['streets'] = models.AbonStreet.objects.filter(group=gid) - context['street_id'] = mydefs.safe_int(self.request.GET.get('street')) + context['street_id'] = lib.safe_int(self.request.GET.get('street')) context['group'] = group return context -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') class GroupListView(BaseAbonListView): context_object_name = 'groups' template_name = 'abonapp/group_list.html' @@ -98,7 +98,7 @@ class GroupListView(BaseAbonListView): return queryset -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') @method_decorator(permission_required('abonapp.add_abon'), name='dispatch') class AbonCreateView(CreateView): group = None @@ -142,9 +142,9 @@ class AbonCreateView(CreateView): messages.success(self.request, _('create abon success msg')) self.abon = abon return super(AbonCreateView, self).form_valid(form) - except (IntegrityError, NasFailedResult, NasNetworkError, mydefs.LogicError) as e: + except (IntegrityError, NasFailedResult, NasNetworkError, lib.LogicError) as e: messages.error(self.request, e) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(self.request, err) return self.render_to_response(self.get_context_data(form=form)) @@ -154,7 +154,7 @@ class AbonCreateView(CreateView): return super(AbonCreateView, self).form_invalid(form) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') @method_decorator(permission_required('abonapp.delete_abon'), name='dispatch') class DelAbonDeleteView(DeleteView): model = models.Abon @@ -181,7 +181,7 @@ class DelAbonDeleteView(DeleteView): messages.error(self.request, e) except NasFailedResult as e: messages.error(self.request, _("NAS says: '%s'") % e) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(self.request, err) return HttpResponseRedirect(self.success_url) @@ -196,7 +196,7 @@ def abonamount(request, gid, uname): if request.method == 'POST': abonuname = request.POST.get('abonuname') if abonuname == uname: - amnt = mydefs.safe_float(request.POST.get('amount')) + amnt = lib.safe_float(request.POST.get('amount')) abon.add_ballance(request.user, amnt, comment=_('fill account through admin side')) abon.save(update_fields=('ballance',)) messages.success(request, _('Account filled successfully on %.2f') % amnt) @@ -205,7 +205,7 @@ def abonamount(request, gid, uname): messages.error(request, _('I not know the account id')) except (NasNetworkError, NasFailedResult) as e: messages.error(request, e) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(request, err) return render_to_text('abonapp/modal_abonamount.html', { @@ -214,7 +214,7 @@ def abonamount(request, gid, uname): }, request=request) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') @method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch') class DebtsListView(BaseAbonListView): context_object_name = 'invoices' @@ -232,7 +232,7 @@ class DebtsListView(BaseAbonListView): return context -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') @method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch') class PayHistoryListView(BaseAbonListView): context_object_name = 'pay_history' @@ -252,7 +252,7 @@ class PayHistoryListView(BaseAbonListView): @login_required -@mydefs.only_admins +@lib.only_admins def abon_services(request, gid, uname): grp = get_object_or_404(Group, pk=gid) if not request.user.has_perm('group_app.can_view_group', grp): @@ -277,7 +277,7 @@ def abon_services(request, gid, uname): }) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') @method_decorator(permission_required('abonapp.change_abon'), name='post') class AbonHomeUpdateView(UpdateView): model = models.Abon @@ -291,11 +291,11 @@ class AbonHomeUpdateView(UpdateView): def dispatch(self, request, *args, **kwargs): try: return super(AbonHomeUpdateView, self).dispatch(request, *args, **kwargs) - except mydefs.LogicError as e: + except lib.LogicError as e: messages.error(request, e) except (NasFailedResult, NasNetworkError) as e: messages.error(request, e) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(request, err) return self.render_to_response(self.get_context_data()) @@ -342,11 +342,11 @@ class AbonHomeUpdateView(UpdateView): def get_context_data(self, **kwargs): abon = self.object - dev = getattr(abon, 'device') + device = getattr(abon, 'device') context = { 'group': self.group, - 'device': dev, - 'dev_ports': DevPort.objects.filter(device=dev) if dev else None + 'device': device, + 'dev_ports': DevPort.objects.filter(device=device) if device else None } context.update(kwargs) return super(AbonHomeUpdateView, self).get_context_data(**context) @@ -354,9 +354,9 @@ class AbonHomeUpdateView(UpdateView): def get_success_url(self): abon = self.object return resolve_url('abonapp:abon_home', - gid=getattr(abon.group, 'pk', 0), - uname=abon.username - ) + gid=getattr(abon.group, 'pk', 0), + uname=abon.username + ) @transaction.atomic @@ -377,7 +377,7 @@ def add_invoice(request, gid, uname): try: if request.method == 'POST': - curr_amount = mydefs.safe_int(request.POST.get('curr_amount')) + curr_amount = lib.safe_int(request.POST.get('curr_amount')) comment = request.POST.get('comment') newinv = models.InvoiceForPayment() @@ -395,7 +395,7 @@ def add_invoice(request, gid, uname): except (NasNetworkError, NasFailedResult) as e: messages.error(request, e) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(request, err) return render(request, 'abonapp/addInvoice.html', { @@ -406,7 +406,7 @@ def add_invoice(request, gid, uname): @login_required -@mydefs.only_admins +@lib.only_admins @permission_required('abonapp.can_buy_tariff') @transaction.atomic def pick_tariff(request, gid, uname): @@ -429,14 +429,14 @@ def pick_tariff(request, gid, uname): abon.sync_with_nas(created=False) messages.success(request, _('Tariff has been picked')) return redirect('abonapp:abon_services', gid=gid, uname=abon.username) - except (mydefs.LogicError, NasFailedResult) as e: + except (lib.LogicError, NasFailedResult) as e: messages.error(request, e) except NasNetworkError as e: messages.error(request, e) return redirect('abonapp:abon_services', gid=gid, uname=abon.username) except Tariff.DoesNotExist: messages.error(request, _('Tariff your picked does not exist')) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(request, err) except ValueError as e: @@ -446,7 +446,7 @@ def pick_tariff(request, gid, uname): 'tariffs': tariffs, 'abon': abon, 'group': grp, - 'selected_tariff': mydefs.safe_int(request.GET.get('selected_tariff')) + 'selected_tariff': lib.safe_int(request.GET.get('selected_tariff')) }) @@ -463,7 +463,7 @@ def unsubscribe_service(request, gid, uname, abon_tariff_id): messages.error(request, e) except NasNetworkError as e: messages.warning(request, e) - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(request, err) return redirect('abonapp:abon_services', gid=gid, uname=uname) @@ -550,7 +550,7 @@ class PassportUpdateView(UpdateView): @login_required -@mydefs.only_admins +@lib.only_admins def chgroup_tariff(request, gid): grp = get_object_or_404(Group, pk=gid) if not request.user.has_perm('group_app.change_group', grp): @@ -578,8 +578,7 @@ def dev(request, gid, uname): try: abon = models.Abon.objects.get(username=uname) if request.method == 'POST': - dev = Device.objects.get(pk=request.POST.get('dev')) - abon.device = dev + abon.device = Device.objects.get(pk=request.POST.get('dev')) abon.save(update_fields=('device',)) messages.success(request, _('Device has successfully attached')) return redirect('abonapp:abon_home', gid=gid, uname=uname) @@ -683,7 +682,7 @@ def make_extra_field(request, gid, uname): except (NasNetworkError, NasFailedResult) as e: messages.error(request, e) frm = forms.ExtraFieldForm() - except mydefs.MultipleException as errs: + except lib.MultipleException as errs: for err in errs.err_list: messages.error(request, err) frm = forms.ExtraFieldForm() @@ -731,7 +730,7 @@ def abon_ping(request): text = ' %s' % _('no ping') try: if ip is None: - raise mydefs.LogicError(_('Ip not passed')) + raise lib.LogicError(_('Ip not passed')) tm = Transmitter() ping_result = tm.ping(ip) if ping_result is None: @@ -756,7 +755,7 @@ def abon_ping(request): text = ' %s' % _('ping ok') + ' ' + str(ping_result) status = True - except (NasFailedResult, mydefs.LogicError) as e: + except (NasFailedResult, lib.LogicError) as e: messages.error(request, e) except NasNetworkError as e: messages.warning(request, e) @@ -767,7 +766,7 @@ def abon_ping(request): } -@method_decorator((login_required, mydefs.only_admins,), name='dispatch') +@method_decorator((login_required, lib.only_admins,), name='dispatch') class DialsListView(BaseAbonListView): context_object_name = 'logs' template_name = 'abonapp/dial_log.html' @@ -813,7 +812,7 @@ def save_user_dev_port(request, gid, uname): if request.method != 'POST': messages.error(request, _('Method is not POST')) return redirect('abonapp:abon_home', gid, uname) - user_port = mydefs.safe_int(request.POST.get('user_port')) + user_port = lib.safe_int(request.POST.get('user_port')) is_dynamic_ip = request.POST.get('is_dynamic_ip') is_dynamic_ip = True if is_dynamic_ip == 'on' else False try: @@ -948,7 +947,7 @@ def tel_add(request, gid, uname): @permission_required('abnapp.delete_additionaltelephone') def tel_del(request, gid, uname): try: - tid = mydefs.safe_int(request.GET.get('tid')) + tid = lib.safe_int(request.GET.get('tid')) tel = models.AdditionalTelephone.objects.get(pk=tid) tel.delete() messages.success(request, _('Additional telephone successfully deleted')) @@ -964,17 +963,17 @@ def phonebook(request, gid): t1 = models.Abon.objects.filter(group__id=int(gid)).only('telephone', 'fio').values_list('telephone', 'fio') t2 = models.AdditionalTelephone.objects.filter(abon__group__id=gid).only('telephone', 'owner_name').values_list( 'telephone', 'owner_name') - tels = list(t1) + list(t2) + telephones = tuple(t1) + tuple(t2) if res_format == 'csv': import csv response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="phones.csv"' writer = csv.writer(response, quoting=csv.QUOTE_NONNUMERIC) - for row in tels: + for row in telephones: writer.writerow(row) return response return render_to_text('abonapp/modal_phonebook.html', { - 'tels': tels, + 'tels': telephones, 'gid': gid }, request=request) @@ -1029,7 +1028,7 @@ def reset_ip(request, gid, uname): @login_required -@mydefs.only_admins +@lib.only_admins def fin_report(request): q = models.AllTimePayLog.objects.by_days() res_format = request.GET.get('f') @@ -1089,7 +1088,7 @@ def del_periodic_pay(request, gid, uname, periodic_pay_id): return redirect('abonapp:abon_services', gid, uname) -@method_decorator((login_required, mydefs.only_admins,), name='dispatch') +@method_decorator((login_required, lib.only_admins,), name='dispatch') class EditSibscriberMarkers(UpdateView): http_method_names = ('get', 'post') template_name = 'abonapp/modal_user_markers.html' @@ -1120,7 +1119,7 @@ class EditSibscriberMarkers(UpdateView): # API's @login_required -@mydefs.only_admins +@lib.only_admins @json_view def abons(request): ablist = ({ @@ -1145,7 +1144,7 @@ def abons(request): @login_required -@mydefs.only_admins +@lib.only_admins @json_view def search_abon(request): word = request.GET.get('s') @@ -1172,15 +1171,14 @@ class DhcpLever(SecureApiView): @staticmethod def on_dhcp_event(data: Dict) -> Optional[str]: - ''' + """ data = { 'client_ip': ip2int('127.0.0.1'), 'client_mac': 'aa:bb:cc:dd:ee:ff', 'switch_mac': 'aa:bb:cc:dd:ee:ff', 'switch_port': 3, 'cmd': 'commit' - } - ''' + }""" r = None try: action = data['cmd'] @@ -1193,7 +1191,7 @@ class DhcpLever(SecureApiView): r = dhcp_expiry(data['client_ip']) elif action == 'release': r = dhcp_release(data['client_ip']) - except mydefs.LogicError as e: + except lib.LogicError as e: print('LogicError', e) r = str(e) return r diff --git a/accounts_app/views.py b/accounts_app/views.py index d0bc04f..6cdd24e 100644 --- a/accounts_app/views.py +++ b/accounts_app/views.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login, logout from django.core.exceptions import PermissionDenied @@ -15,7 +14,7 @@ from group_app.models import Group from .models import UserProfile from .forms import AvatarChangeForm -import mydefs +from djing import lib from guardian.decorators import permission_required_or_403 as permission_required from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm @@ -62,9 +61,9 @@ class SignOut(RedirectView): @login_required -@mydefs.only_admins +@lib.only_admins def profile_show(request, uid=0): - uid = mydefs.safe_int(uid) + uid = lib.safe_int(uid) if uid == 0: return redirect('acc_app:other_profile', uid=request.user.id) @@ -87,7 +86,7 @@ def profile_show(request, uid=0): }) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') class AvatarUpdateView(UpdateView): form_class = AvatarChangeForm template_name = 'accounts/settings/ch_info.html' @@ -100,7 +99,7 @@ class AvatarUpdateView(UpdateView): @login_required -@mydefs.only_admins +@lib.only_admins def ch_info(request): if request.method == 'POST': user = request.user @@ -169,7 +168,7 @@ def create_profile(request): @login_required -@mydefs.only_admins +@lib.only_admins def delete_profile(request, uid): prf = get_object_or_404(UserProfile, id=uid) if uid != request.user.id: @@ -180,7 +179,7 @@ def delete_profile(request, uid): return redirect('acc_app:accounts_list') -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') class AccountsListView(BaseAccListView): template_name = 'accounts/acc_list.html' context_object_name = 'users' @@ -288,7 +287,7 @@ def set_abon_groups_permission(request, uid): }) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') class ManageResponsibilityGroups(ListView): http_method_names = ('get', 'post') template_name = 'accounts/manage_responsibility_groups.html' diff --git a/agent/mod_mikrotik.py b/agent/mod_mikrotik.py index 03adb24..3f246ce 100644 --- a/agent/mod_mikrotik.py +++ b/agent/mod_mikrotik.py @@ -6,7 +6,7 @@ from abc import ABCMeta from hashlib import md5 from typing import Iterable, Optional, Tuple from .core import BaseTransmitter, NasFailedResult, NasNetworkError -from mydefs import singleton +from djing.lib import singleton from .structs import TariffStruct, AbonStruct, IpStruct, VectorAbon, VectorTariff from . import settings as local_settings from django.conf import settings diff --git a/agent/structs.py b/agent/structs.py index 13f85b2..39c7bac 100644 --- a/agent/structs.py +++ b/agent/structs.py @@ -1,18 +1,18 @@ # -*- coding: utf-8 -*- from abc import ABCMeta, abstractmethod from struct import pack, unpack, calcsize -from typing import Iterable -from .utils import int2ip, ip2int +from typing import Iterable, Optional +from djing.lib import int2ip, ip2int class BaseStruct(object, metaclass=ABCMeta): @abstractmethod - def serialize(self): - """привращаем инфу в бинарную строку""" + def serialize(self) -> Optional[bytes]: + """make binary""" @abstractmethod - def deserialize(self, data, *args): - """создаём объект из бинарной строки""" + def deserialize(self, data: bytes, *args): + """restore from binary""" def __ne__(self, other): return not self == other @@ -25,11 +25,11 @@ class IpStruct(BaseStruct): else: self.__ip = ip2int(str(ip)) - def serialize(self): + def serialize(self) -> Optional[bytes]: dt = pack("!I", int(self.__ip)) return dt - def deserialize(self, data, *args): + def deserialize(self, data: bytes, *args): dt = unpack("!I", data) self.__ip = int(dt[0]) return self @@ -54,12 +54,12 @@ class IpStruct(BaseStruct): # Как обслуживается абонент class TariffStruct(BaseStruct): - def __init__(self, tariff_id=0, speedIn=None, speedOut=None): + def __init__(self, tariff_id=0, speed_in=None, speed_out=None): self.tid = int(tariff_id) - self.speedIn = float(speedIn if speedIn is not None else 0.001) - self.speedOut = float(speedOut if speedOut is not None else 0.001) + self.speedIn = speed_in or 0 + self.speedOut = speed_out or 0 - def serialize(self): + def serialize(self) -> Optional[bytes]: dt = pack("!Iff", int(self.tid), float(self.speedIn), float(self.speedOut)) return dt @@ -67,7 +67,7 @@ class TariffStruct(BaseStruct): def is_empty(self): return self.tid == 0 and self.speedIn == 0.001 and self.speedOut == 0.001 - def deserialize(self, data, *args): + def deserialize(self, data: bytes, *args): dt = unpack("!Iff", data) self.tid = int(dt[0]) self.speedIn = float(dt[1]) @@ -96,7 +96,7 @@ class AbonStruct(BaseStruct): self.tariff = tariff self.is_active = is_active - def serialize(self): + def serialize(self) -> Optional[bytes]: if self.tariff is None: return if not isinstance(self.tariff, TariffStruct): @@ -106,7 +106,7 @@ class AbonStruct(BaseStruct): dt = pack("!LII?", self.uid, int(self.ip), self.tariff.tid, self.is_active) return dt - def deserialize(self, data, tariff=None): + def deserialize(self, data: bytes, tariff=None): dt = unpack("!LII?", data) self.uid = dt[0] self.ip = IpStruct(dt[1]) @@ -137,12 +137,12 @@ class ShapeItem(BaseStruct): self.abon = abon self.sid = sid - def serialize(self): + def serialize(self) -> Optional[bytes]: abon_pack = self.abon.serialize() dt = pack('!L', self.sid) return dt + abon_pack - def deserialize(self, data, *args): + def deserialize(self, data: bytes, *args): sz = calcsize('!L') dt = unpack('!L', data[:sz]) self.sid = dt diff --git a/clientsideapp/views.py b/clientsideapp/views.py index f95b796..68f9cfe 100644 --- a/clientsideapp/views.py +++ b/clientsideapp/views.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _, gettext from abonapp.models import AbonLog, InvoiceForPayment, Abon from tariff_app.models import Tariff from taskapp.models import Task -from mydefs import LogicError +from djing.lib import LogicError from agent import NasFailedResult, NasNetworkError diff --git a/devapp/migrations/0001_initial.py b/devapp/migrations/0001_initial.py index fa24063..9103f5f 100644 --- a/devapp/migrations/0001_initial.py +++ b/devapp/migrations/0001_initial.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion import djing.fields -import mydefs +from djing.lib import MyGenericIPAddressField class Migration(migrations.Migration): @@ -21,7 +21,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ( - 'ip_address', mydefs.MyGenericIPAddressField(max_length=8, protocol='ipv4', verbose_name='Ip address')), + 'ip_address', MyGenericIPAddressField(max_length=8, protocol='ipv4', verbose_name='Ip address')), ('mac_addr', djing.fields.MACAddressField(blank=True, integer=True, null=True, unique=True, verbose_name='Mac address')), ('comment', models.CharField(max_length=256, verbose_name='Comment')), diff --git a/devapp/migrations/0002_auto_20180409_1318.py b/devapp/migrations/0002_auto_20180409_1318.py index ea0fad0..891cd0c 100644 --- a/devapp/migrations/0002_auto_20180409_1318.py +++ b/devapp/migrations/0002_auto_20180409_1318.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import migrations -import mydefs +from djing.lib import MyGenericIPAddressField class Migration(migrations.Migration): @@ -20,7 +20,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='device', name='ip_address', - field=mydefs.MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4', + field=MyGenericIPAddressField(blank=True, max_length=8, null=True, protocol='ipv4', verbose_name='Ip address'), ), ] diff --git a/dialing_app/views.py b/dialing_app/views.py index ce5c203..0108d26 100644 --- a/dialing_app/views.py +++ b/dialing_app/views.py @@ -16,7 +16,7 @@ from jsonview.decorators import json_view from abonapp.models import Abon from djing.global_base_views import SecureApiView from djing import JSONType -from mydefs import only_admins, safe_int +from djing.lib import only_admins, safe_int from .models import AsteriskCDR, SMSModel, SMSOut from .forms import SMSOutForm diff --git a/djing/lib/__init__.py b/djing/lib/__init__.py new file mode 100644 index 0000000..948f5d5 --- /dev/null +++ b/djing/lib/__init__.py @@ -0,0 +1,186 @@ +import socket +import struct +from datetime import timedelta +from collections import Iterator +from functools import wraps +from json import dumps +from django.http import HttpResponse, Http404, HttpResponseRedirect +from django.shortcuts import redirect +from django.db import models +from django.conf import settings + +DEBUG = getattr(settings, 'DEBUG', False) + + +def ip2int(addr): + try: + return struct.unpack("!I", socket.inet_aton(addr))[0] + except: + return 0 + + +def int2ip(addr): + try: + return socket.inet_ntoa(struct.pack("!I", addr)) + except: + return '' + + +def safe_float(fl): + try: + return 0.0 if fl is None or fl == '' else float(fl) + except ValueError: + return 0.0 + + +def safe_int(i): + try: + return 0 if i is None or i == '' else int(i) + except ValueError: + return 0 + + +def res_success(request, redirect_to='/'): + if request.is_ajax(): + return HttpResponse(dumps({'errnum': 0})) + else: + return redirect(redirect_to) + + +def res_error(request, text): + if request.is_ajax(): + return HttpResponse(dumps({'errnum': 1, 'errtext': text})) + else: + raise Http404(text) + + +class MyGenericIPAddressField(models.GenericIPAddressField): + description = "Int32 notation ip address" + + def __init__(self, protocol='ipv4', *args, **kwargs): + super(MyGenericIPAddressField, self).__init__(protocol=protocol, *args, **kwargs) + self.max_length = 8 + + def get_prep_value(self, value): + # strIp to Int + value = super(MyGenericIPAddressField, self).get_prep_value(value) + return ip2int(value) + + def to_python(self, value): + return value + + def get_internal_type(self): + return 'PositiveIntegerField' + + @staticmethod + def from_db_value(value, expression, connection, context): + return int2ip(value) if value != 0 else None + + def int_ip(self): + return ip2int(self) + + +# Предназначен для Django CHOICES чтоб можно было передавать классы вместо просто описания поля, +# классы передавать для того чтоб по значению кода из базы понять какой класс нужно взять для нужной функциональности. +# Например по коду в базе вам нужно определять как считать тариф абонента, что реализовано в возвращаемом классе. +class MyChoicesAdapter(Iterator): + chs = tuple() + current_index = 0 + _max_index = 0 + + # На вход принимает кортеж кортежей, вложенный из 2х элементов: кода и класса, как: TARIFF_CHOICES + def __init__(self, choices): + self._max_index = len(choices) + self.chs = choices + + def __next__(self): + if self.current_index >= self._max_index: + raise StopIteration + else: + e = self.chs + ci = self.current_index + res = e[ci][0], e[ci][1].description() + self.current_index += 1 + return res + + +# Allow to view only admins +def only_admins(fn): + @wraps(fn) + def wrapped(request, *args, **kwargs): + if request.user.is_admin: + return fn(request, *args, **kwargs) + else: + return redirect('client_side:home') + + return wrapped + + +# Russian localized timedelta +class RuTimedelta(timedelta): + def __new__(cls, tm): + if isinstance(tm, timedelta): + return timedelta.__new__( + cls, + days=tm.days, + seconds=tm.seconds, + microseconds=tm.microseconds + ) + + def __str__(self): + # hours, remainder = divmod(self.seconds, 3600) + # minutes, seconds = divmod(remainder, 60) + # text_date = "%d:%d" % ( + # hours, + # minutes + # ) + if self.days > 1: + ru_days = 'дней' + if 5 > self.days > 1: + ru_days = 'дня' + elif self.days == 1: + ru_days = 'день' + # text_date = '%d %s %s' % (self.days, ru_days, text_date) + text_date = '%d %s' % (self.days, ru_days) + else: + text_date = '' + return text_date + + +def require_ssl(view): + """ + Decorator that requires an SSL connection. If the current connection is not SSL, we redirect to the SSL version of + the page. + from: https://gist.github.com/ckinsey/9709984 + """ + + @wraps(view) + def wrapper(request, *args, **kwargs): + if not DEBUG and not request.is_secure(): + target_url = "https://" + request.META['HTTP_HOST'] + request.path_info + return HttpResponseRedirect(target_url) + return view(request, *args, **kwargs) + + return wrapper + + +class MultipleException(Exception): + def __init__(self, err_list): + if not isinstance(err_list, (list, tuple)): + raise TypeError + self.err_list = err_list + + +class LogicError(Exception): + pass + + +def singleton(class_): + instances = {} + + def getinstance(*args, **kwargs): + if class_ not in instances: + instances[class_] = class_(*args, **kwargs) + return instances[class_] + + return getinstance diff --git a/djing/settings.py b/djing/settings.py index 4b38677..d73a627 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -25,7 +25,7 @@ ALLOWED_HOSTS = local_settings.ALLOWED_HOSTS # required for django-guardian AUTHENTICATION_BACKENDS = ( - 'djing.auth_backends.CustomAuthBackend', + 'djing.lib.auth_backends.CustomAuthBackend', # 'django.contrib.auth.backends.ModelBackend', # default 'guardian.backends.ObjectPermissionBackend' ) diff --git a/docs/dev.md b/docs/dev.md index 09d00e4..ce2fff9 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -343,7 +343,7 @@ def check_news(request): return HttpResponse(dumps(r)) ``` -Убедитесь что вашему представлению не будет доступа от абонентов, об этом позаботится декоратор *only_admins* из *mydefs*. *mydefs* лежит в корне проекта. +Убедитесь что вашему представлению не будет доступа от абонентов, об этом позаботится декоратор *only_admins* из *djing.lib*. После получения сообщения надо вернуть словарь с параметрами: diff --git a/mapapp/views.py b/mapapp/views.py index f996f42..1b61c28 100644 --- a/mapapp/views.py +++ b/mapapp/views.py @@ -14,7 +14,7 @@ from jsonview.decorators import json_view from group_app.models import Group from .models import Dot from .forms import DotForm -from mydefs import safe_int +from djing.lib import safe_int from devapp.models import Device from guardian.decorators import permission_required diff --git a/periodic.py b/periodic.py index 6d7b4cc..0657bb5 100755 --- a/periodic.py +++ b/periodic.py @@ -9,7 +9,7 @@ from django.db import transaction from django.db.models import signals from abonapp.models import Abon, AbonTariff, abontariff_pre_delete, PeriodicPayForId, AbonLog from agent import Transmitter, NasNetworkError, NasFailedResult -from mydefs import LogicError +from djing.lib import LogicError def main(): diff --git a/statistics/migrations/0001_initial.py b/statistics/migrations/0001_initial.py index bd83354..4148c97 100644 --- a/statistics/migrations/0001_initial.py +++ b/statistics/migrations/0001_initial.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.db import migrations, models -import mydefs +from djing.lib import MyGenericIPAddressField import statistics.fields @@ -18,8 +18,7 @@ class Migration(migrations.Migration): name='StatCache', fields=[ ('last_time', statistics.fields.UnixDateTimeField()), - ( - 'ip', mydefs.MyGenericIPAddressField(max_length=8, primary_key=True, protocol='ipv4', serialize=False)), + ('ip', MyGenericIPAddressField(max_length=8, primary_key=True, protocol='ipv4', serialize=False)), ('octets', models.PositiveIntegerField(default=0)), ('packets', models.PositiveIntegerField(default=0)), ], diff --git a/statistics/models.py b/statistics/models.py index c5bc191..13ff2b4 100644 --- a/statistics/models.py +++ b/statistics/models.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta, date, time from django.db import models, connection, ProgrammingError from django.utils.timezone import now -from mydefs import MyGenericIPAddressField +from djing.lib import MyGenericIPAddressField from .fields import UnixDateTimeField diff --git a/tariff_app/base_intr.py b/tariff_app/base_intr.py index cd2f8aa..970dbe9 100644 --- a/tariff_app/base_intr.py +++ b/tariff_app/base_intr.py @@ -17,7 +17,7 @@ class TariffBase(metaclass=ABCMeta): @staticmethod def description() -> AnyStr: """ - Usage in mydefs.MyChoicesAdapter for choices fields. + Usage in djing.lib.MyChoicesAdapter for choices fields. :return: human readable description """ raise NotImplementedError @@ -54,5 +54,5 @@ class PeriodicPayCalcBase(metaclass=ABCMeta): @staticmethod def description() -> AnyStr: """Return text description. - Uses in mydefs.MyChoicesAdapter for CHOICES fields""" + Uses in djing.lib.MyChoicesAdapter for CHOICES fields""" raise NotImplementedError diff --git a/tariff_app/models.py b/tariff_app/models.py index 3cb82cb..6ca5e04 100644 --- a/tariff_app/models.py +++ b/tariff_app/models.py @@ -5,7 +5,7 @@ from django.dispatch import receiver from .base_intr import TariffBase, PeriodicPayCalcBase from .custom_tariffs import TARIFF_CHOICES, PERIODIC_PAY_CHOICES from group_app.models import Group -from mydefs import MyChoicesAdapter +from djing.lib import MyChoicesAdapter from jsonfield import JSONField diff --git a/tariff_app/views.py b/tariff_app/views.py index 9dbdaaa..1f0d7d9 100644 --- a/tariff_app/views.py +++ b/tariff_app/views.py @@ -12,7 +12,7 @@ from guardian.decorators import permission_required_or_403 as permission_require from djing.global_base_views import OrderingMixin from .models import Tariff, PeriodicPay -import mydefs +from djing import lib from . import forms @@ -21,7 +21,7 @@ class BaseServiceListView(ListView): paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) -@method_decorator((login_required, mydefs.only_admins), name='dispatch') +@method_decorator((login_required, lib.only_admins), name='dispatch') class TariffsListView(BaseServiceListView, OrderingMixin): """ Show Services(Tariffs) list @@ -33,7 +33,7 @@ class TariffsListView(BaseServiceListView, OrderingMixin): @login_required def edit_tarif(request, tarif_id=0): - tarif_id = mydefs.safe_int(tarif_id) + tarif_id = lib.safe_int(tarif_id) if tarif_id == 0: if not request.user.has_perm('tariff_app.add_tariff'): @@ -70,7 +70,7 @@ def del_tarif(request, tid): messages.success(request, _('Service has been deleted')) else: messages.error(request, _('Not have a confirmations of delete')) - return mydefs.res_success(request, 'tarifs:home') + return lib.res_success(request, 'tarifs:home') return render_to_text('tariff_app/modal_del_warning.html', {'tid': tid}, request=request) diff --git a/taskapp/handle.py b/taskapp/handle.py index c973dc3..cabcdfc 100644 --- a/taskapp/handle.py +++ b/taskapp/handle.py @@ -2,7 +2,7 @@ from django.utils.translation import gettext as _ from chatbot.telebot import send_notify from chatbot.models import ChatException -from mydefs import MultipleException +from djing.lib import MultipleException class TaskException(Exception): diff --git a/taskapp/migrations/0001_initial.py b/taskapp/migrations/0001_initial.py index 06da89c..fe69535 100644 --- a/taskapp/migrations/0001_initial.py +++ b/taskapp/migrations/0001_initial.py @@ -51,7 +51,7 @@ class Migration(migrations.Migration): ('priority', models.CharField(choices=[('A', 'Higher'), ('C', 'Average'), ('E', 'Low')], default='E', max_length=1, verbose_name='A priority')), - ('out_date', models.DateField(blank=True, default=taskapp.models._delta_add_days, null=True, + ('out_date', models.DateField(blank=True, default=taskapp.models.delta_add_days, null=True, verbose_name='Reality')), ('time_of_create', models.DateTimeField(auto_now_add=True, verbose_name='Date of create')), ('state', models.CharField(choices=[('S', 'New'), ('C', 'Confused'), ('F', 'Completed')], default='S', diff --git a/taskapp/views.py b/taskapp/views.py index 949e521..b0624a8 100644 --- a/taskapp/views.py +++ b/taskapp/views.py @@ -1,5 +1,3 @@ -# coding=utf-8 -from json import dumps from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied from django.http import HttpResponse @@ -15,12 +13,14 @@ from datetime import datetime from django.views.generic.edit import FormMixin, DeleteView, UpdateView from guardian.decorators import permission_required_or_403 as permission_required +from jsonview.decorators import json_view + from chatbot.models import MessageQueue from abonapp.models import Abon from djing import httpresponse_to_referrer +from djing.lib import only_admins, safe_int, MultipleException, RuTimedelta from .handle import TaskException from .models import Task, ExtraComment -from mydefs import only_admins, safe_int, MultipleException, RuTimedelta from .forms import TaskFrm, ExtraCommentForm @@ -249,6 +249,7 @@ def remind(request, task_id): return redirect('taskapp:home') +@json_view def check_news(request): if request.user.is_authenticated and request.user.is_admin: msg = MessageQueue.objects.pop(user=request.user, tag='taskap')