Browse Source

Merge branch 'devel' of https://github.com/nerosketch/djing into devel

devel
www-data 8 years ago
parent
commit
eb33f6ad65
  1. 8
      abonapp/forms.py
  2. 4
      abonapp/migrations/0001_initial.py
  3. 2
      abonapp/models.py
  4. 2
      abonapp/pay_systems.py
  5. 22
      abonapp/templates/abonapp/abon_confirm_delete.html
  6. 106
      abonapp/views.py
  7. 18
      accounts_app/views.py
  8. 11
      agent/mod_mikrotik.py
  9. 34
      agent/structs.py
  10. 2
      clientsideapp/views.py
  11. 15
      devapp/base_intr.py
  12. 92
      devapp/dev_types.py
  13. 18
      devapp/forms.py
  14. 424
      devapp/locale/ru/LC_MESSAGES/django.po
  15. 4
      devapp/migrations/0001_initial.py
  16. 4
      devapp/migrations/0002_auto_20180409_1318.py
  17. 60
      devapp/migrations/0003_auto_20180529_1311.py
  18. 17
      devapp/models.py
  19. 2
      devapp/templates/devapp/add_dev.html
  20. 2
      devapp/templates/devapp/custom_dev_page/olt_ztec320_ports.html
  21. 19
      devapp/templates/devapp/custom_dev_page/onu.html
  22. 91
      devapp/templates/devapp/custom_dev_page/onu_for_zte.html
  23. 4
      devapp/templates/devapp/dev.html
  24. 14
      devapp/templates/devapp/device_confirm_delete.html
  25. 4
      devapp/templates/devapp/devices.html
  26. 4
      devapp/templates/devapp/devices_null_group.html
  27. 2
      devapp/urls.py
  28. 49
      devapp/views.py
  29. 2
      dialing.py
  30. 3
      dialing_app/views.py
  31. 70
      djing/lib/__init__.py
  32. 0
      djing/lib/auth_backends.py
  33. 35
      djing/lib/decorators.py
  34. 0
      djing/lib/messaging/__init__.py
  35. 7
      djing/lib/messaging/sms/__init__.py
  36. 0
      djing/lib/messaging/sms/base.py
  37. 0
      djing/lib/messaging/sms/consts.py
  38. 10
      djing/lib/messaging/sms/deliver.py
  39. 0
      djing/lib/messaging/sms/gsm0338.py
  40. 0
      djing/lib/messaging/sms/pdu.py
  41. 18
      djing/lib/messaging/sms/submit.py
  42. 0
      djing/lib/messaging/sms/udh.py
  43. 2
      djing/lib/messaging/sms/wap.py
  44. 0
      djing/lib/messaging/utils.py
  45. 4
      djing/lib/tln/__init__.py
  46. 266
      djing/lib/tln/tln.py
  47. 2
      djing/settings.py
  48. 2
      docs/dev.md
  49. 25
      group_app/templates/group_app/group_confirm_delete.html
  50. 2
      mapapp/views.py
  51. 7
      messaging/sms/__init__.py
  52. 359
      migrate_to_0.2.py
  53. 2
      periodic.py
  54. 18
      static/css/custom.css
  55. 5
      statistics/migrations/0001_initial.py
  56. 2
      statistics/models.py
  57. 4
      tariff_app/base_intr.py
  58. 3
      tariff_app/locale/ru/LC_MESSAGES/django.po
  59. 2
      tariff_app/models.py
  60. 30
      tariff_app/templates/tariff_app/modal_del_warning.html
  61. 15
      tariff_app/templates/tariff_app/tariff_confirm_delete.html
  62. 2
      tariff_app/urls.py
  63. 38
      tariff_app/views.py
  64. 2
      taskapp/handle.py
  65. 2
      taskapp/migrations/0001_initial.py
  66. 8
      taskapp/views.py
  67. 19
      templates/base_delete_modal.html

8
abonapp/forms.py

@ -98,12 +98,6 @@ class AbonForm(forms.ModelForm):
class PassportForm(forms.ModelForm): class PassportForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs.update({'initial': {
'date_of_acceptance': datetime(year=2014, month=6, day=1).strftime("%Y-%m-%d")
}})
super(PassportForm, self).__init__(*args, **kwargs)
class Meta: class Meta:
model = models.PassportInfo model = models.PassportInfo
exclude = ('abon',) exclude = ('abon',)
@ -111,7 +105,7 @@ class PassportForm(forms.ModelForm):
'series': forms.TextInput(attrs={'required': '', 'pattern': '^\d{4}$'}), 'series': forms.TextInput(attrs={'required': '', 'pattern': '^\d{4}$'}),
'number': forms.TextInput(attrs={'required': '', 'pattern': '^\d{6}$'}), 'number': forms.TextInput(attrs={'required': '', 'pattern': '^\d{6}$'}),
'distributor': forms.TextInput(attrs={'required': ''}), 'distributor': forms.TextInput(attrs={'required': ''}),
'date_of_acceptance': forms.DateInput(attrs={'class': 'form-control', 'required': ''})
'date_of_acceptance': forms.DateInput(attrs={'class': 'form-control', 'required': ''}, format='%Y-%m-%d')
} }

4
abonapp/migrations/0001_initial.py

@ -7,7 +7,7 @@ from django.conf import settings
import django.core.validators import django.core.validators
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import mydefs
from djing import lib
import re 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, models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True,
primary_key=True, serialize=False, to='accounts_app.BaseAccount')), primary_key=True, serialize=False, to='accounts_app.BaseAccount')),
('ballance', models.FloatField(default=0.0)), ('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')), ('description', models.TextField(blank=True, null=True, verbose_name='Comment')),
('house', models.CharField(blank=True, max_length=12, null=True, verbose_name='House')), ('house', models.CharField(blank=True, max_length=12, null=True, verbose_name='House')),
('is_dynamic_ip', models.BooleanField(default=False)), ('is_dynamic_ip', models.BooleanField(default=False)),

2
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 accounts_app.models import UserProfile, MyUserManager, BaseAccount
from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult, NasNetworkError
from group_app.models import Group 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 djing import IP_ADDR_REGEX
from tariff_app.models import Tariff, PeriodicPay from tariff_app.models import Tariff, PeriodicPay
from bitfield import BitField from bitfield import BitField

2
abonapp/pay_systems.py

@ -1,6 +1,6 @@
from hashlib import md5 from hashlib import md5
from django.utils import timezone 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 .models import Abon, AllTimePayLog
from django.db import DatabaseError from django.db import DatabaseError
from django.conf import settings from django.conf import settings

22
abonapp/templates/abonapp/abon_confirm_delete.html

@ -1,18 +1,10 @@
{% extends 'base_delete_modal.html' %}
{% load i18n %} {% load i18n %}
<form role="form" action="{% url 'abonapp:del_abon' abon.group.pk abon.username %}" method="post">{% csrf_token %}
<div class="modal-header warning">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-earphone"></span>{% trans 'Remove subscriber' %}</h4>
</div>
<div class="modal-body">
{% block modal_form_url %}
{% url 'abonapp:del_abon' abon.group.pk abon.username %}
{% endblock %}
<h4>{% trans 'Are you sure about them?' %}</h4>
<button type="submit" class="btn btn-danger" value="DELETE">
<span class="glyphicon glyphicon-remove"></span> {% trans 'Delete' %}
</button>
</div>
</form>
{% block modal_form_title %}
{% trans 'Remove subscriber' %}
{% endblock %}

106
abonapp/views.py

@ -20,7 +20,6 @@ from tariff_app.models import Tariff
from agent import NasFailedResult, Transmitter, NasNetworkError from agent import NasFailedResult, Transmitter, NasNetworkError
from . import forms from . import forms
from . import models from . import models
import mydefs
from devapp.models import Device, Port as DevPort from devapp.models import Device, Port as DevPort
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
from taskapp.models import Task 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.shortcuts import get_objects_for_user, assign_perm
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
from djing import ping from djing import ping
from djing import lib
from djing.global_base_views import OrderingMixin, BaseListWithFiltering, SecureApiView from djing.global_base_views import OrderingMixin, BaseListWithFiltering, SecureApiView
PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
@ -40,14 +40,14 @@ class BaseAbonListView(OrderingMixin, BaseListWithFiltering):
http_method_names = ('get',) http_method_names = ('get',)
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
class PeoplesListView(BaseAbonListView): class PeoplesListView(BaseAbonListView):
context_object_name = 'peoples' context_object_name = 'peoples'
template_name = 'abonapp/peoples.html' template_name = 'abonapp/peoples.html'
def get_queryset(self): 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') peoples_list = models.Abon.objects.all().select_related('group', 'street', 'current_tariff')
if street_id > 0: if street_id > 0:
peoples_list = peoples_list.filter(group__pk=gid, street=street_id) 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) abon.stat_cache = StatCache.objects.get(ip=abon.ip_address)
except StatCache.DoesNotExist: except StatCache.DoesNotExist:
pass pass
except mydefs.LogicError as e:
except lib.LogicError as e:
messages.warning(self.request, e) messages.warning(self.request, e)
ordering = self.get_ordering() ordering = self.get_ordering()
if ordering and isinstance(ordering, str): if ordering and isinstance(ordering, str):
@ -70,7 +70,7 @@ class PeoplesListView(BaseAbonListView):
return peoples_list return peoples_list
def get_context_data(self, **kwargs): 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: if gid < 1:
return HttpResponseBadRequest('group id is broken') return HttpResponseBadRequest('group id is broken')
group = get_object_or_404(Group, pk=gid) group = get_object_or_404(Group, pk=gid)
@ -80,12 +80,12 @@ class PeoplesListView(BaseAbonListView):
context = super(PeoplesListView, self).get_context_data(**kwargs) context = super(PeoplesListView, self).get_context_data(**kwargs)
context['streets'] = models.AbonStreet.objects.filter(group=gid) 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 context['group'] = group
return context return context
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
class GroupListView(BaseAbonListView): class GroupListView(BaseAbonListView):
context_object_name = 'groups' context_object_name = 'groups'
template_name = 'abonapp/group_list.html' template_name = 'abonapp/group_list.html'
@ -98,7 +98,7 @@ class GroupListView(BaseAbonListView):
return queryset return queryset
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('abonapp.add_abon'), name='dispatch') @method_decorator(permission_required('abonapp.add_abon'), name='dispatch')
class AbonCreateView(CreateView): class AbonCreateView(CreateView):
group = None group = None
@ -142,9 +142,9 @@ class AbonCreateView(CreateView):
messages.success(self.request, _('create abon success msg')) messages.success(self.request, _('create abon success msg'))
self.abon = abon self.abon = abon
return super(AbonCreateView, self).form_valid(form) 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) messages.error(self.request, e)
except mydefs.MultipleException as errs:
except lib.MultipleException as errs:
for err in errs.err_list: for err in errs.err_list:
messages.error(self.request, err) messages.error(self.request, err)
return self.render_to_response(self.get_context_data(form=form)) 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) return super(AbonCreateView, self).form_invalid(form)
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('abonapp.delete_abon'), name='dispatch') @method_decorator(permission_required('abonapp.delete_abon'), name='dispatch')
class DelAbonDeleteView(DeleteView): class DelAbonDeleteView(DeleteView):
model = models.Abon model = models.Abon
@ -181,7 +181,7 @@ class DelAbonDeleteView(DeleteView):
messages.error(self.request, e) messages.error(self.request, e)
except NasFailedResult as e: except NasFailedResult as e:
messages.error(self.request, _("NAS says: '%s'") % 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: for err in errs.err_list:
messages.error(self.request, err) messages.error(self.request, err)
return HttpResponseRedirect(self.success_url) return HttpResponseRedirect(self.success_url)
@ -196,7 +196,7 @@ def abonamount(request, gid, uname):
if request.method == 'POST': if request.method == 'POST':
abonuname = request.POST.get('abonuname') abonuname = request.POST.get('abonuname')
if abonuname == uname: 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.add_ballance(request.user, amnt, comment=_('fill account through admin side'))
abon.save(update_fields=('ballance',)) abon.save(update_fields=('ballance',))
messages.success(request, _('Account filled successfully on %.2f') % amnt) 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')) messages.error(request, _('I not know the account id'))
except (NasNetworkError, NasFailedResult) as e: except (NasNetworkError, NasFailedResult) as e:
messages.error(request, e) messages.error(request, e)
except mydefs.MultipleException as errs:
except lib.MultipleException as errs:
for err in errs.err_list: for err in errs.err_list:
messages.error(request, err) messages.error(request, err)
return render_to_text('abonapp/modal_abonamount.html', { return render_to_text('abonapp/modal_abonamount.html', {
@ -214,7 +214,7 @@ def abonamount(request, gid, uname):
}, request=request) }, request=request)
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch') @method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class DebtsListView(BaseAbonListView): class DebtsListView(BaseAbonListView):
context_object_name = 'invoices' context_object_name = 'invoices'
@ -232,7 +232,7 @@ class DebtsListView(BaseAbonListView):
return context return context
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch') @method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class PayHistoryListView(BaseAbonListView): class PayHistoryListView(BaseAbonListView):
context_object_name = 'pay_history' context_object_name = 'pay_history'
@ -252,7 +252,7 @@ class PayHistoryListView(BaseAbonListView):
@login_required @login_required
@mydefs.only_admins
@lib.decorators.only_admins
def abon_services(request, gid, uname): def abon_services(request, gid, uname):
grp = get_object_or_404(Group, pk=gid) grp = get_object_or_404(Group, pk=gid)
if not request.user.has_perm('group_app.can_view_group', grp): 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.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('abonapp.change_abon'), name='post') @method_decorator(permission_required('abonapp.change_abon'), name='post')
class AbonHomeUpdateView(UpdateView): class AbonHomeUpdateView(UpdateView):
model = models.Abon model = models.Abon
@ -291,11 +291,11 @@ class AbonHomeUpdateView(UpdateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
try: try:
return super(AbonHomeUpdateView, self).dispatch(request, *args, **kwargs) return super(AbonHomeUpdateView, self).dispatch(request, *args, **kwargs)
except mydefs.LogicError as e:
except lib.LogicError as e:
messages.error(request, e) messages.error(request, e)
except (NasFailedResult, NasNetworkError) as e: except (NasFailedResult, NasNetworkError) as e:
messages.error(request, e) messages.error(request, e)
except mydefs.MultipleException as errs:
except lib.MultipleException as errs:
for err in errs.err_list: for err in errs.err_list:
messages.error(request, err) messages.error(request, err)
return self.render_to_response(self.get_context_data()) return self.render_to_response(self.get_context_data())
@ -342,11 +342,11 @@ class AbonHomeUpdateView(UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
abon = self.object abon = self.object
dev = getattr(abon, 'device')
device = getattr(abon, 'device')
context = { context = {
'group': self.group, '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) context.update(kwargs)
return super(AbonHomeUpdateView, self).get_context_data(**context) return super(AbonHomeUpdateView, self).get_context_data(**context)
@ -354,9 +354,9 @@ class AbonHomeUpdateView(UpdateView):
def get_success_url(self): def get_success_url(self):
abon = self.object abon = self.object
return resolve_url('abonapp:abon_home', 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 @transaction.atomic
@ -377,7 +377,7 @@ def add_invoice(request, gid, uname):
try: try:
if request.method == 'POST': 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') comment = request.POST.get('comment')
newinv = models.InvoiceForPayment() newinv = models.InvoiceForPayment()
@ -395,7 +395,7 @@ def add_invoice(request, gid, uname):
except (NasNetworkError, NasFailedResult) as e: except (NasNetworkError, NasFailedResult) as e:
messages.error(request, e) messages.error(request, e)
except mydefs.MultipleException as errs:
except lib.MultipleException as errs:
for err in errs.err_list: for err in errs.err_list:
messages.error(request, err) messages.error(request, err)
return render(request, 'abonapp/addInvoice.html', { return render(request, 'abonapp/addInvoice.html', {
@ -406,7 +406,7 @@ def add_invoice(request, gid, uname):
@login_required @login_required
@mydefs.only_admins
@lib.decorators.only_admins
@permission_required('abonapp.can_buy_tariff') @permission_required('abonapp.can_buy_tariff')
@transaction.atomic @transaction.atomic
def pick_tariff(request, gid, uname): def pick_tariff(request, gid, uname):
@ -429,14 +429,14 @@ def pick_tariff(request, gid, uname):
abon.sync_with_nas(created=False) abon.sync_with_nas(created=False)
messages.success(request, _('Tariff has been picked')) messages.success(request, _('Tariff has been picked'))
return redirect('abonapp:abon_services', gid=gid, uname=abon.username) 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) messages.error(request, e)
except NasNetworkError as e: except NasNetworkError as e:
messages.error(request, e) messages.error(request, e)
return redirect('abonapp:abon_services', gid=gid, uname=abon.username) return redirect('abonapp:abon_services', gid=gid, uname=abon.username)
except Tariff.DoesNotExist: except Tariff.DoesNotExist:
messages.error(request, _('Tariff your picked does not exist')) 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: for err in errs.err_list:
messages.error(request, err) messages.error(request, err)
except ValueError as e: except ValueError as e:
@ -446,7 +446,7 @@ def pick_tariff(request, gid, uname):
'tariffs': tariffs, 'tariffs': tariffs,
'abon': abon, 'abon': abon,
'group': grp, '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) messages.error(request, e)
except NasNetworkError as e: except NasNetworkError as e:
messages.warning(request, e) messages.warning(request, e)
except mydefs.MultipleException as errs:
except lib.MultipleException as errs:
for err in errs.err_list: for err in errs.err_list:
messages.error(request, err) messages.error(request, err)
return redirect('abonapp:abon_services', gid=gid, uname=uname) return redirect('abonapp:abon_services', gid=gid, uname=uname)
@ -550,7 +550,7 @@ class PassportUpdateView(UpdateView):
@login_required @login_required
@mydefs.only_admins
@lib.decorators.only_admins
def chgroup_tariff(request, gid): def chgroup_tariff(request, gid):
grp = get_object_or_404(Group, pk=gid) grp = get_object_or_404(Group, pk=gid)
if not request.user.has_perm('group_app.change_group', grp): if not request.user.has_perm('group_app.change_group', grp):
@ -578,8 +578,7 @@ def dev(request, gid, uname):
try: try:
abon = models.Abon.objects.get(username=uname) abon = models.Abon.objects.get(username=uname)
if request.method == 'POST': 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',)) abon.save(update_fields=('device',))
messages.success(request, _('Device has successfully attached')) messages.success(request, _('Device has successfully attached'))
return redirect('abonapp:abon_home', gid=gid, uname=uname) 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: except (NasNetworkError, NasFailedResult) as e:
messages.error(request, e) messages.error(request, e)
frm = forms.ExtraFieldForm() frm = forms.ExtraFieldForm()
except mydefs.MultipleException as errs:
except lib.MultipleException as errs:
for err in errs.err_list: for err in errs.err_list:
messages.error(request, err) messages.error(request, err)
frm = forms.ExtraFieldForm() frm = forms.ExtraFieldForm()
@ -731,7 +730,7 @@ def abon_ping(request):
text = '<span class="glyphicon glyphicon-exclamation-sign"></span> %s' % _('no ping') text = '<span class="glyphicon glyphicon-exclamation-sign"></span> %s' % _('no ping')
try: try:
if ip is None: if ip is None:
raise mydefs.LogicError(_('Ip not passed'))
raise lib.LogicError(_('Ip not passed'))
tm = Transmitter() tm = Transmitter()
ping_result = tm.ping(ip) ping_result = tm.ping(ip)
if ping_result is None: if ping_result is None:
@ -756,7 +755,7 @@ def abon_ping(request):
text = '<span class="glyphicon glyphicon-ok"></span> %s' % _('ping ok') + ' ' + str(ping_result) text = '<span class="glyphicon glyphicon-ok"></span> %s' % _('ping ok') + ' ' + str(ping_result)
status = True status = True
except (NasFailedResult, mydefs.LogicError) as e:
except (NasFailedResult, lib.LogicError) as e:
messages.error(request, e) messages.error(request, e)
except NasNetworkError as e: except NasNetworkError as e:
messages.warning(request, 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.decorators.only_admins,), name='dispatch')
class DialsListView(BaseAbonListView): class DialsListView(BaseAbonListView):
context_object_name = 'logs' context_object_name = 'logs'
template_name = 'abonapp/dial_log.html' template_name = 'abonapp/dial_log.html'
@ -813,7 +812,7 @@ def save_user_dev_port(request, gid, uname):
if request.method != 'POST': if request.method != 'POST':
messages.error(request, _('Method is not POST')) messages.error(request, _('Method is not POST'))
return redirect('abonapp:abon_home', gid, uname) 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 = request.POST.get('is_dynamic_ip')
is_dynamic_ip = True if is_dynamic_ip == 'on' else False is_dynamic_ip = True if is_dynamic_ip == 'on' else False
try: try:
@ -948,7 +947,7 @@ def tel_add(request, gid, uname):
@permission_required('abnapp.delete_additionaltelephone') @permission_required('abnapp.delete_additionaltelephone')
def tel_del(request, gid, uname): def tel_del(request, gid, uname):
try: 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 = models.AdditionalTelephone.objects.get(pk=tid)
tel.delete() tel.delete()
messages.success(request, _('Additional telephone successfully deleted')) 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') 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( t2 = models.AdditionalTelephone.objects.filter(abon__group__id=gid).only('telephone', 'owner_name').values_list(
'telephone', 'owner_name') 'telephone', 'owner_name')
tels = list(t1) + list(t2)
telephones = tuple(t1) + tuple(t2)
if res_format == 'csv': if res_format == 'csv':
import csv import csv
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="phones.csv"' response['Content-Disposition'] = 'attachment; filename="phones.csv"'
writer = csv.writer(response, quoting=csv.QUOTE_NONNUMERIC) writer = csv.writer(response, quoting=csv.QUOTE_NONNUMERIC)
for row in tels:
for row in telephones:
writer.writerow(row) writer.writerow(row)
return response return response
return render_to_text('abonapp/modal_phonebook.html', { return render_to_text('abonapp/modal_phonebook.html', {
'tels': tels,
'tels': telephones,
'gid': gid 'gid': gid
}, request=request) }, request=request)
@ -1029,7 +1028,7 @@ def reset_ip(request, gid, uname):
@login_required @login_required
@mydefs.only_admins
@lib.decorators.only_admins
def fin_report(request): def fin_report(request):
q = models.AllTimePayLog.objects.by_days() q = models.AllTimePayLog.objects.by_days()
res_format = request.GET.get('f') 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) return redirect('abonapp:abon_services', gid, uname)
@method_decorator((login_required, mydefs.only_admins,), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins,), name='dispatch')
class EditSibscriberMarkers(UpdateView): class EditSibscriberMarkers(UpdateView):
http_method_names = ('get', 'post') http_method_names = ('get', 'post')
template_name = 'abonapp/modal_user_markers.html' template_name = 'abonapp/modal_user_markers.html'
@ -1120,7 +1119,7 @@ class EditSibscriberMarkers(UpdateView):
# API's # API's
@login_required @login_required
@mydefs.only_admins
@lib.decorators.only_admins
@json_view @json_view
def abons(request): def abons(request):
ablist = ({ ablist = ({
@ -1145,7 +1144,7 @@ def abons(request):
@login_required @login_required
@mydefs.only_admins
@lib.decorators.only_admins
@json_view @json_view
def search_abon(request): def search_abon(request):
word = request.GET.get('s') word = request.GET.get('s')
@ -1172,15 +1171,14 @@ class DhcpLever(SecureApiView):
@staticmethod @staticmethod
def on_dhcp_event(data: Dict) -> Optional[str]: def on_dhcp_event(data: Dict) -> Optional[str]:
'''
"""
data = { data = {
'client_ip': ip2int('127.0.0.1'), 'client_ip': ip2int('127.0.0.1'),
'client_mac': 'aa:bb:cc:dd:ee:ff', 'client_mac': 'aa:bb:cc:dd:ee:ff',
'switch_mac': 'aa:bb:cc:dd:ee:ff', 'switch_mac': 'aa:bb:cc:dd:ee:ff',
'switch_port': 3, 'switch_port': 3,
'cmd': 'commit' 'cmd': 'commit'
}
'''
}"""
r = None r = None
try: try:
action = data['cmd'] action = data['cmd']
@ -1193,7 +1191,7 @@ class DhcpLever(SecureApiView):
r = dhcp_expiry(data['client_ip']) r = dhcp_expiry(data['client_ip'])
elif action == 'release': elif action == 'release':
r = dhcp_release(data['client_ip']) r = dhcp_release(data['client_ip'])
except mydefs.LogicError as e:
except lib.LogicError as e:
print('LogicError', e) print('LogicError', e)
r = str(e) r = str(e)
return r return r

18
accounts_app/views.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@ -15,7 +14,8 @@ from group_app.models import Group
from .models import UserProfile from .models import UserProfile
from .forms import AvatarChangeForm from .forms import AvatarChangeForm
import mydefs
from djing import lib
from djing.lib.decorators import only_admins
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm
@ -62,9 +62,9 @@ class SignOut(RedirectView):
@login_required @login_required
@mydefs.only_admins
@only_admins
def profile_show(request, uid=0): def profile_show(request, uid=0):
uid = mydefs.safe_int(uid)
uid = lib.safe_int(uid)
if uid == 0: if uid == 0:
return redirect('acc_app:other_profile', uid=request.user.id) return redirect('acc_app:other_profile', uid=request.user.id)
@ -87,7 +87,7 @@ def profile_show(request, uid=0):
}) })
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, only_admins), name='dispatch')
class AvatarUpdateView(UpdateView): class AvatarUpdateView(UpdateView):
form_class = AvatarChangeForm form_class = AvatarChangeForm
template_name = 'accounts/settings/ch_info.html' template_name = 'accounts/settings/ch_info.html'
@ -100,7 +100,7 @@ class AvatarUpdateView(UpdateView):
@login_required @login_required
@mydefs.only_admins
@only_admins
def ch_info(request): def ch_info(request):
if request.method == 'POST': if request.method == 'POST':
user = request.user user = request.user
@ -169,7 +169,7 @@ def create_profile(request):
@login_required @login_required
@mydefs.only_admins
@only_admins
def delete_profile(request, uid): def delete_profile(request, uid):
prf = get_object_or_404(UserProfile, id=uid) prf = get_object_or_404(UserProfile, id=uid)
if uid != request.user.id: if uid != request.user.id:
@ -180,7 +180,7 @@ def delete_profile(request, uid):
return redirect('acc_app:accounts_list') return redirect('acc_app:accounts_list')
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, only_admins), name='dispatch')
class AccountsListView(BaseAccListView): class AccountsListView(BaseAccListView):
template_name = 'accounts/acc_list.html' template_name = 'accounts/acc_list.html'
context_object_name = 'users' context_object_name = 'users'
@ -288,7 +288,7 @@ def set_abon_groups_permission(request, uid):
}) })
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, only_admins), name='dispatch')
class ManageResponsibilityGroups(ListView): class ManageResponsibilityGroups(ListView):
http_method_names = ('get', 'post') http_method_names = ('get', 'post')
template_name = 'accounts/manage_responsibility_groups.html' template_name = 'accounts/manage_responsibility_groups.html'

11
agent/mod_mikrotik.py

@ -6,7 +6,7 @@ from abc import ABCMeta
from hashlib import md5 from hashlib import md5
from typing import Iterable, Optional, Tuple from typing import Iterable, Optional, Tuple
from .core import BaseTransmitter, NasFailedResult, NasNetworkError from .core import BaseTransmitter, NasFailedResult, NasNetworkError
from mydefs import singleton
from djing.lib import Singleton
from .structs import TariffStruct, AbonStruct, IpStruct, VectorAbon, VectorTariff from .structs import TariffStruct, AbonStruct, IpStruct, VectorAbon, VectorTariff
from . import settings as local_settings from . import settings as local_settings
from django.conf import settings from django.conf import settings
@ -19,8 +19,7 @@ LIST_USERS_ALLOWED = 'DjingUsersAllowed'
#LIST_USERS_BLOCKED = 'DjingUsersBlocked' #LIST_USERS_BLOCKED = 'DjingUsersBlocked'
@singleton
class ApiRos:
class ApiRos(metaclass=Singleton):
"""Routeros api""" """Routeros api"""
sk = None sk = None
is_login = False is_login = False
@ -224,8 +223,8 @@ class TransmitterManager(BaseTransmitter, metaclass=ABCMeta):
speeds = info['=max-limit'].split('/') speeds = info['=max-limit'].split('/')
t = TariffStruct( t = TariffStruct(
speedIn=parse_speed(speeds[1]),
speedOut=parse_speed(speeds[0])
speed_in=parse_speed(speeds[1]),
speed_out=parse_speed(speeds[0])
) )
try: try:
a = AbonStruct( a = AbonStruct(
@ -483,7 +482,7 @@ class MikrotikTransmitter(QueueManager, IpAddressListManager):
interface = r[0]['=interface'] interface = r[0]['=interface']
r = self._exec_cmd(( r = self._exec_cmd((
'/ping', '=address=%s' % host, '=arp-ping=yes', '=interval=100ms', '=count=%d' % count, '/ping', '=address=%s' % host, '=arp-ping=yes', '=interval=100ms', '=count=%d' % count,
'=interface=%s' % interface
'=interface=%s' % interface
)) ))
received, sent = int(r[-2:][0]['=received']), int(r[-2:][0]['=sent']) received, sent = int(r[-2:][0]['=received']), int(r[-2:][0]['=sent'])
return received, sent return received, sent

34
agent/structs.py

@ -1,18 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from struct import pack, unpack, calcsize 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): class BaseStruct(object, metaclass=ABCMeta):
@abstractmethod @abstractmethod
def serialize(self):
"""привращаем инфу в бинарную строку"""
def serialize(self) -> Optional[bytes]:
"""make binary"""
@abstractmethod @abstractmethod
def deserialize(self, data, *args):
"""создаём объект из бинарной строки"""
def deserialize(self, data: bytes, *args):
"""restore from binary"""
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other
@ -25,11 +25,11 @@ class IpStruct(BaseStruct):
else: else:
self.__ip = ip2int(str(ip)) self.__ip = ip2int(str(ip))
def serialize(self):
def serialize(self) -> Optional[bytes]:
dt = pack("!I", int(self.__ip)) dt = pack("!I", int(self.__ip))
return dt return dt
def deserialize(self, data, *args):
def deserialize(self, data: bytes, *args):
dt = unpack("!I", data) dt = unpack("!I", data)
self.__ip = int(dt[0]) self.__ip = int(dt[0])
return self return self
@ -54,12 +54,12 @@ class IpStruct(BaseStruct):
# Как обслуживается абонент # Как обслуживается абонент
class TariffStruct(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.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)) dt = pack("!Iff", int(self.tid), float(self.speedIn), float(self.speedOut))
return dt return dt
@ -67,7 +67,7 @@ class TariffStruct(BaseStruct):
def is_empty(self): def is_empty(self):
return self.tid == 0 and self.speedIn == 0.001 and self.speedOut == 0.001 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) dt = unpack("!Iff", data)
self.tid = int(dt[0]) self.tid = int(dt[0])
self.speedIn = float(dt[1]) self.speedIn = float(dt[1])
@ -96,7 +96,7 @@ class AbonStruct(BaseStruct):
self.tariff = tariff self.tariff = tariff
self.is_active = is_active self.is_active = is_active
def serialize(self):
def serialize(self) -> Optional[bytes]:
if self.tariff is None: if self.tariff is None:
return return
if not isinstance(self.tariff, TariffStruct): 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) dt = pack("!LII?", self.uid, int(self.ip), self.tariff.tid, self.is_active)
return dt return dt
def deserialize(self, data, tariff=None):
def deserialize(self, data: bytes, tariff=None):
dt = unpack("!LII?", data) dt = unpack("!LII?", data)
self.uid = dt[0] self.uid = dt[0]
self.ip = IpStruct(dt[1]) self.ip = IpStruct(dt[1])
@ -137,12 +137,12 @@ class ShapeItem(BaseStruct):
self.abon = abon self.abon = abon
self.sid = sid self.sid = sid
def serialize(self):
def serialize(self) -> Optional[bytes]:
abon_pack = self.abon.serialize() abon_pack = self.abon.serialize()
dt = pack('!L', self.sid) dt = pack('!L', self.sid)
return dt + abon_pack return dt + abon_pack
def deserialize(self, data, *args):
def deserialize(self, data: bytes, *args):
sz = calcsize('!L') sz = calcsize('!L')
dt = unpack('!L', data[:sz]) dt = unpack('!L', data[:sz])
self.sid = dt self.sid = dt

2
clientsideapp/views.py

@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _, gettext
from abonapp.models import AbonLog, InvoiceForPayment, Abon from abonapp.models import AbonLog, InvoiceForPayment, Abon
from tariff_app.models import Tariff from tariff_app.models import Tariff
from taskapp.models import Task from taskapp.models import Task
from mydefs import LogicError
from djing.lib import LogicError
from agent import NasFailedResult, NasNetworkError from agent import NasFailedResult, NasNetworkError

15
devapp/base_intr.py

@ -1,10 +1,10 @@
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from datetime import timedelta from datetime import timedelta
from django.utils.translation import gettext
from typing import Union, Iterable, AnyStr, Generator, Optional from typing import Union, Iterable, AnyStr, Generator, Optional
from easysnmp import Session from easysnmp import Session
from django.utils.translation import gettext
ListOrError = Union[ ListOrError = Union[
Iterable, Iterable,
Union[Exception, Iterable] Union[Exception, Iterable]
@ -15,7 +15,7 @@ class DeviceImplementationError(Exception):
pass pass
class DevBase(object, metaclass=ABCMeta):
class DevBase(object):
def __init__(self, dev_instance=None): def __init__(self, dev_instance=None):
self.db_instance = dev_instance self.db_instance = dev_instance
@ -53,6 +53,15 @@ class DevBase(object, metaclass=ABCMeta):
def is_use_device_port() -> bool: def is_use_device_port() -> bool:
"""True if used device port while opt82 authorization""" """True if used device port while opt82 authorization"""
@abstractmethod
def validate_extra_snmp_info(self, v: str) -> None:
"""
Validate extra snmp field for each device.
If validation failed then raise en exception from djing.lib.tln.ValidationError
with description of error.
:param v: String value for validate
"""
class BasePort(object, metaclass=ABCMeta): class BasePort(object, metaclass=ABCMeta):
def __init__(self, num, name, status, mac, speed): def __init__(self, num, name, status, mac, speed):

92
devapp/dev_types.py

@ -1,9 +1,10 @@
from typing import AnyStr, Iterable
from typing import AnyStr, Iterable, Optional, Dict
from datetime import timedelta from datetime import timedelta
from easysnmp import EasySNMPTimeoutError from easysnmp import EasySNMPTimeoutError
from django.utils.translation import gettext_lazy as _, gettext from django.utils.translation import gettext_lazy as _, gettext
from mydefs import RuTimedelta, safe_int
from djing.lib import RuTimedelta, safe_int
from djing.lib.tln.tln import ValidationError as TlnValidationError
from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError, ListOrError
@ -77,6 +78,10 @@ class DLinkDevice(DevBase, SNMPBaseWorker):
def is_use_device_port(): def is_use_device_port():
return True return True
def validate_extra_snmp_info(self, v: str) -> None:
# Dlink has no require snmp info
pass
class ONUdev(BasePort): class ONUdev(BasePort):
def __init__(self, num, name, status, mac, speed, signal, snmp_worker): def __init__(self, num, name, status, mac, speed, signal, snmp_worker):
@ -150,6 +155,10 @@ class OLTDevice(DevBase, SNMPBaseWorker):
def is_use_device_port(): def is_use_device_port():
return False return False
def validate_extra_snmp_info(self, v: str) -> None:
# Olt has no require snmp info
pass
class OnuDevice(DevBase, SNMPBaseWorker): class OnuDevice(DevBase, SNMPBaseWorker):
def __init__(self, dev_instance): def __init__(self, dev_instance):
@ -197,7 +206,7 @@ class OnuDevice(DevBase, SNMPBaseWorker):
def get_details(self): def get_details(self):
if self.db_instance is None: if self.db_instance is None:
return return
num = self.db_instance.snmp_item_num
num = safe_int(self.db_instance.snmp_extra)
if num == 0: if num == 0:
return return
try: try:
@ -216,6 +225,13 @@ class OnuDevice(DevBase, SNMPBaseWorker):
except EasySNMPTimeoutError as e: except EasySNMPTimeoutError as e:
return {'err': "%s: %s" % (_('ONU not connected'), e)} return {'err': "%s: %s" % (_('ONU not connected'), e)}
def validate_extra_snmp_info(self, v: str) -> None:
# DBCOM Onu have en integer snmp port
try:
int(v)
except ValueError:
raise TlnValidationError(_('Onu snmp field must be en integer'))
class EltexPort(BasePort): class EltexPort(BasePort):
def __init__(self, snmp_worker, *args, **kwargs): def __init__(self, snmp_worker, *args, **kwargs):
@ -272,8 +288,17 @@ class EltexSwitch(DLinkDevice):
return False return False
class Olt_ZTE_C320(OLTDevice):
def conv_signal(lvl: int) -> float:
if lvl == 65535: return 0.0
r = 0
if 0 < lvl < 30000:
r = lvl * 0.002 - 30
elif 60000 < lvl < 65534:
r = (lvl - 65534) * 0.002 - 30
return round(r, 2)
class Olt_ZTE_C320(OLTDevice):
@staticmethod @staticmethod
def description(): def description():
return gettext('OLT ZTE C320') return gettext('OLT ZTE C320')
@ -287,16 +312,8 @@ class Olt_ZTE_C320(OLTDevice):
return fibers return fibers
def get_ports_on_fiber(self, fiber_num: int) -> Iterable: def get_ports_on_fiber(self, fiber_num: int) -> Iterable:
def conv_signal(lvl: int) -> float:
if lvl == 65535: return 0.0
r = 0
if 0 < lvl < 30000:
r = lvl * 0.002 - 30
elif 60000 < lvl < 65534:
r = (lvl - 65534) * 0.002 - 30
return round(r, 2)
onu_types = self.get_list('.1.3.6.1.4.1.3902.1012.3.28.1.1.1.%d' % fiber_num)
onu_types = self.get_list_keyval('.1.3.6.1.4.1.3902.1012.3.28.1.1.1.%d' % fiber_num)
onu_ports = self.get_list('.1.3.6.1.4.1.3902.1012.3.28.1.1.2.%d' % fiber_num) onu_ports = self.get_list('.1.3.6.1.4.1.3902.1012.3.28.1.1.2.%d' % fiber_num)
onu_signals = self.get_list('.1.3.6.1.4.1.3902.1012.3.50.12.1.1.10.%d' % fiber_num) onu_signals = self.get_list('.1.3.6.1.4.1.3902.1012.3.50.12.1.1.10.%d' % fiber_num)
@ -304,13 +321,15 @@ class Olt_ZTE_C320(OLTDevice):
onu_sns = self.get_list('.1.3.6.1.4.1.3902.1012.3.28.1.1.5.%d' % fiber_num) onu_sns = self.get_list('.1.3.6.1.4.1.3902.1012.3.28.1.1.5.%d' % fiber_num)
onu_prefixs = self.get_list('.1.3.6.1.4.1.3902.1012.3.50.11.2.1.1.%d' % fiber_num) onu_prefixs = self.get_list('.1.3.6.1.4.1.3902.1012.3.50.11.2.1.1.%d' % fiber_num)
onu_list = ({ onu_list = ({
'onu_type': onu_type,
'onu_type': onu_type_num[0],
'onu_port': onu_port, 'onu_port': onu_port,
'onu_signal': conv_signal(safe_int(onu_signal)), 'onu_signal': conv_signal(safe_int(onu_signal)),
'onu_sn': onu_prefix + ''.join('%.2X' % ord(i) for i in onu_sn[-4:]), # Real sn in last 4 octets
} for onu_type, onu_port, onu_signal, onu_sn, onu_prefix in zip(
'onu_sn': onu_prefix + ''.join('%.2X' % ord(i) for i in onu_sn[-4:]), # Real sn in last 4 octets,
'snmp_extra': "%d.%d" % (fiber_num, safe_int(onu_type_num[1])),
} for onu_type_num, onu_port, onu_signal, onu_sn, onu_prefix in zip(
onu_types, onu_ports, onu_signals, onu_sns, onu_prefixs onu_types, onu_ports, onu_signals, onu_sns, onu_prefixs
)) ))
return onu_list return onu_list
def uptime(self): def uptime(self):
@ -326,3 +345,42 @@ class Olt_ZTE_C320(OLTDevice):
def get_template_name(self): def get_template_name(self):
return 'olt_ztec320.html' return 'olt_ztec320.html'
class ZteOnuDevice(OnuDevice):
@staticmethod
def description():
return _('ZTE PON ONU')
def get_details(self) -> Optional[Dict]:
if self.db_instance is None:
return
snmp_extra = self.db_instance.snmp_extra
if not snmp_extra:
return
try:
fiber_num, onu_num = snmp_extra.split('.')
fiber_num, onu_num = int(fiber_num), int(onu_num)
status = self.get_item('.1.3.6.1.4.1.3902.1012.3.50.12.1.1.1.%d.%d.1' % (fiber_num, onu_num))
signal = self.get_item('.1.3.6.1.4.1.3902.1012.3.50.12.1.1.10.%d.%d.1' % (fiber_num, onu_num))
distance = self.get_item('.1.3.6.1.4.1.3902.1012.3.50.12.1.1.18.%d.%d.1' % (fiber_num, onu_num))
name = self.get_item('.1.3.6.1.4.1.3902.1012.3.50.11.2.1.1.%d.%d' % (fiber_num, onu_num))
return {
'status': status,
'signal': conv_signal(safe_int(signal)),
'name': name,
'distance': int(distance) / 10 if distance != 'NOSUCHINSTANCE' else 0
}
except ValueError:
pass
def get_template_name(self):
return 'onu_for_zte.html'
def validate_extra_snmp_info(self, v: str) -> None:
# for example 268501760.5
try:
fiber_num, onu_port = v.split('.')
int(fiber_num), int(onu_port)
except ValueError:
raise TlnValidationError(_('Zte onu snmp field must be two dot separated integers'))

18
devapp/forms.py

@ -1,7 +1,9 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db import IntegrityError from django.db import IntegrityError
from djing.lib.tln.tln import ValidationError as TlnValidationError
from . import models from . import models
from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX from djing import MAC_ADDR_REGEX, IP_ADDR_REGEX
@ -15,9 +17,23 @@ class DeviceForm(forms.ModelForm):
'unique': _('Device with that mac is already exist') 'unique': _('Device with that mac is already exist')
}) })
def clean_snmp_extra(self):
snmp_extra = self.cleaned_data.get('snmp_extra')
if snmp_extra is None:
return
device = self.instance
manager = device.get_manager_object()
try:
manager.validate_extra_snmp_info(snmp_extra)
except TlnValidationError as e:
raise ValidationError(
e, code='invalid'
)
return snmp_extra
class Meta: class Meta:
model = models.Device model = models.Device
exclude = ['map_dot', 'status']
exclude = ('map_dot', 'status')
widgets = { widgets = {
'ip_address': forms.TextInput(attrs={ 'ip_address': forms.TextInput(attrs={
'pattern': IP_ADDR_REGEX, 'pattern': IP_ADDR_REGEX,

424
devapp/locale/ru/LC_MESSAGES/django.po

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-24 23:27+0300\n"
"POT-Creation-Date: 2018-06-02 16:39+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" "Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n" "Language: ru\n"
@ -18,377 +18,456 @@ msgstr ""
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n" "%100>=11 && n%100<=14)? 2 : 3);\n"
#: devapp/base_intr.py:82
#: base_intr.py:92
msgid "Ip address is required" msgid "Ip address is required"
msgstr "Ip адрес необходим" msgstr "Ip адрес необходим"
#: devapp/dev_types.py:35
#: dev_types.py:37
msgid "DLink switch" msgid "DLink switch"
msgstr "Свич D'Link" msgstr "Свич D'Link"
#: devapp/dev_types.py:54
#: dev_types.py:56
msgid "does not fetch the mac" msgid "does not fetch the mac"
msgstr "не нашёл мак" msgstr "не нашёл мак"
#: devapp/dev_types.py:106
#: dev_types.py:112
msgid "PON OLT" msgid "PON OLT"
msgstr "" msgstr ""
#: devapp/dev_types.py:131 devapp/views.py:268 devapp/views.py:413
#: dev_types.py:136 views.py:275 views.py:433
msgid "wait for a reply from the SNMP Timeout" msgid "wait for a reply from the SNMP Timeout"
msgstr "Время ожидания ответа от SNMP истекло" msgstr "Время ожидания ответа от SNMP истекло"
#: devapp/dev_types.py:171
#: dev_types.py:176
msgid "Ip address or parent device with ip address required for ONU device"
msgstr ""
"Необходим ip адрес. Или родительское устройство с назначенным ip адресом "
"необходимо."
#: dev_types.py:182
msgid "PON ONU" msgid "PON ONU"
msgstr "" msgstr ""
#: devapp/dev_types.py:216
#: dev_types.py:227
msgid "ONU not connected" msgid "ONU not connected"
msgstr "ONU не в сети" msgstr "ONU не в сети"
#: devapp/dev_types.py:242
#: dev_types.py:234
msgid "Onu snmp field must be en integer"
msgstr "Поле для snmp информации об ONU должно быть числом"
#: dev_types.py:260
msgid "Eltex switch" msgid "Eltex switch"
msgstr "Элтекс свич" msgstr "Элтекс свич"
#: devapp/forms.py:14
#: dev_types.py:305
msgid "OLT ZTE C320"
msgstr ""
#: dev_types.py:354
msgid "ZTE PON ONU"
msgstr ""
#: dev_types.py:387
msgid "Zte onu snmp field must be two dot separated integers"
msgstr ""
"Поле snmp информации для ZTE ONU должно быть двумя целыми числами, "
"разделенными точкой"
#: forms.py:16
msgid "Mac address is required for fill" msgid "Mac address is required for fill"
msgstr "MAC-адрес необходим для заполнения" msgstr "MAC-адрес необходим для заполнения"
#: devapp/forms.py:15
#: forms.py:17
msgid "Device with that mac is already exist" msgid "Device with that mac is already exist"
msgstr "Устройство с этим мак-адресом уже есть" msgstr "Устройство с этим мак-адресом уже есть"
#: devapp/forms.py:48
#: forms.py:63
msgid "Port number on device must be unique" msgid "Port number on device must be unique"
msgstr "Номер порта на устройстве должен быть уникальным" msgstr "Номер порта на устройстве должен быть уникальным"
#: devapp/models.py:21 devapp/templates/devapp/custom_dev_page/onu.html:18
#: devapp/templates/devapp/devices.html:22
#: devapp/templates/devapp/devices_null_group.html:20
#: models.py:25 templates/devapp/custom_dev_page/onu.html:18
#: templates/devapp/custom_dev_page/onu_for_zte.html:18
#: templates/devapp/devices.html:22 templates/devapp/devices_null_group.html:20
msgid "Ip address" msgid "Ip address"
msgstr "Ip адрес" msgstr "Ip адрес"
#: devapp/models.py:22 devapp/templates/devapp/devices.html:32
#: models.py:26 templates/devapp/devices.html:32
msgid "Mac address" msgid "Mac address"
msgstr "Мак адрес" msgstr "Мак адрес"
#: devapp/models.py:23 devapp/templates/devapp/devices.html:28
#: devapp/templates/devapp/devices_null_group.html:26
#: models.py:27 templates/devapp/devices.html:28
#: templates/devapp/devices_null_group.html:26
msgid "Comment" msgid "Comment"
msgstr "Комментарий" msgstr "Комментарий"
#: devapp/models.py:30 devapp/templates/devapp/devices.html:36
#: devapp/templates/devapp/devices_null_group.html:32
#: models.py:36 templates/devapp/devices.html:36
#: templates/devapp/devices_null_group.html:32
msgid "Device type" msgid "Device type"
msgstr "Тип устройства" msgstr "Тип устройства"
#: devapp/models.py:32
#: models.py:38
msgid "SNMP password" msgid "SNMP password"
msgstr "Пароль SNMP" msgstr "Пароль SNMP"
#: devapp/models.py:33
#: models.py:39
msgid "Device group" msgid "Device group"
msgstr "Группа устройства" msgstr "Группа устройства"
#: devapp/models.py:34 devapp/templates/devapp/add_dev.html:46
#: devapp/templates/devapp/custom_dev_page/onu.html:34
#: devapp/templates/devapp/custom_dev_page/ports.html:59
#: devapp/templates/devapp/dev.html:33
#: devapp/templates/devapp/fix_dev_group.html:42
#: models.py:40 templates/devapp/add_dev.html:46
#: templates/devapp/custom_dev_page/onu.html:34
#: templates/devapp/custom_dev_page/onu_for_zte.html:34
#: templates/devapp/custom_dev_page/ports.html:59 templates/devapp/dev.html:33
#: templates/devapp/fix_dev_group.html:42
msgid "Parent device" msgid "Parent device"
msgstr "Родительское устройство" msgstr "Родительское устройство"
#: devapp/models.py:37
msgid "SNMP Number"
msgstr "SNMP Ном."
#: models.py:43
msgid "SNMP extra info"
msgstr "Доп. инфо для snmp"
#: devapp/models.py:40
#: models.py:46
msgid "Undefined" msgid "Undefined"
msgstr "Не определено" msgstr "Не определено"
#: devapp/models.py:41
#: models.py:47
msgid "Up" msgid "Up"
msgstr "В сети" msgstr "В сети"
#: devapp/models.py:42
#: models.py:48
msgid "Unreachable" msgid "Unreachable"
msgstr "Не доступно" msgstr "Не доступно"
#: devapp/models.py:43
#: models.py:49
msgid "Down" msgid "Down"
msgstr "Не в сети" msgstr "Не в сети"
#: devapp/models.py:45
#: models.py:51
msgid "Status" msgid "Status"
msgstr "Состояние" msgstr "Состояние"
#: devapp/models.py:47
#: models.py:53
msgid "Send notify when monitoring state changed" msgid "Send notify when monitoring state changed"
msgstr "Отправлять уведомления при событиях мониторинга" msgstr "Отправлять уведомления при событиях мониторинга"
#: devapp/models.py:52
#: models.py:58
msgid "Can view device" msgid "Can view device"
msgstr "Может видеть устройство" msgstr "Может видеть устройство"
#: devapp/models.py:54 devapp/models.py:126
#: models.py:60 models.py:108
msgid "Device" msgid "Device"
msgstr "Устройство" msgstr "Устройство"
#: devapp/models.py:55 devapp/templates/devapp/devices.html:14
#: devapp/templates/devapp/devices_null_group.html:8
#: models.py:61 templates/devapp/devices.html:14
#: templates/devapp/devices_null_group.html:8
msgid "Devices" msgid "Devices"
msgstr "Устройства" msgstr "Устройства"
#: devapp/models.py:127 devapp/templates/devapp/manage_ports/list.html:11
#: models.py:96 views.py:175 views.py:237
msgid "Device does not have a group, please fix that"
msgstr "У устройства нет группы, пожалуйста, исправьте это"
#: models.py:109 templates/devapp/manage_ports/list.html:11
msgid "Number" msgid "Number"
msgstr "Номер" msgstr "Номер"
#: devapp/models.py:128 devapp/templates/devapp/custom_dev_page/onu.html:20
#: devapp/templates/devapp/custom_dev_page/ports.html:88
#: devapp/templates/devapp/manage_ports/add_ports.html:33
#: devapp/templates/devapp/manage_ports/list.html:12
#: models.py:110 templates/devapp/custom_dev_page/onu.html:20
#: templates/devapp/custom_dev_page/onu_for_zte.html:20
#: templates/devapp/custom_dev_page/ports.html:88
#: templates/devapp/manage_ports/add_ports.html:33
#: templates/devapp/manage_ports/list.html:12
msgid "Description" msgid "Description"
msgstr "Описание" msgstr "Описание"
#: devapp/models.py:137
#: models.py:119
msgid "Can toggle ports" msgid "Can toggle ports"
msgstr "Может переключать порты" msgstr "Может переключать порты"
#: devapp/models.py:139 devapp/templates/devapp/custom_dev_page/ports.html:110
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:24
#: models.py:121 templates/devapp/custom_dev_page/ports.html:110
#: templates/devapp/manage_ports/fix_abon_device.html:24
msgid "Port" msgid "Port"
msgstr "Порт" msgstr "Порт"
#: devapp/models.py:140
#: models.py:122
msgid "Ports" msgid "Ports"
msgstr "Порты" msgstr "Порты"
#: devapp/templates/devapp/add_dev.html:8
#: devapp/templates/devapp/devices.html:8
#: devapp/templates/devapp/devices_null_group.html:7
#: devapp/templates/devapp/fix_dev_group.html:9
#: devapp/templates/devapp/group_list.html:7
#: devapp/templates/devapp/manage_ports/add_ports.html:7
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:7
#: templates/devapp/add_dev.html:8
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:7
#: templates/devapp/devices.html:8 templates/devapp/devices_null_group.html:7
#: templates/devapp/fix_dev_group.html:9 templates/devapp/group_list.html:7
#: templates/devapp/manage_ports/add_ports.html:7
#: templates/devapp/manage_ports/fix_abon_device.html:7
msgid "Groups" msgid "Groups"
msgstr "Группы" msgstr "Группы"
#: devapp/templates/devapp/add_dev.html:10
#: templates/devapp/add_dev.html:10
msgid "Add new device" msgid "Add new device"
msgstr "Добавить устройство" msgstr "Добавить устройство"
#: devapp/templates/devapp/add_dev.html:16
#: devapp/templates/devapp/custom_dev_page/ports.html:68
#: devapp/templates/devapp/devices.html:63
#: devapp/templates/devapp/manage_ports/add_ports.html:16
#: templates/devapp/add_dev.html:16
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:12
#: templates/devapp/custom_dev_page/ports.html:68
#: templates/devapp/devices.html:63
#: templates/devapp/manage_ports/add_ports.html:16
msgid "Not assigned" msgid "Not assigned"
msgstr "&lt;Не назначено&gt;" msgstr "&lt;Не назначено&gt;"
#: devapp/templates/devapp/add_dev.html:21 devapp/templates/devapp/dev.html:8
#: templates/devapp/add_dev.html:21 templates/devapp/dev.html:8
msgid "Device info" msgid "Device info"
msgstr "Инфа о железке" msgstr "Инфа о железке"
#: devapp/templates/devapp/add_dev.html:60 devapp/templates/devapp/dev.html:43
#: devapp/templates/devapp/dev.html:47
#: devapp/templates/devapp/fix_dev_group.html:51
#: devapp/templates/devapp/fix_dev_group.html:55
#: templates/devapp/add_dev.html:60 templates/devapp/dev.html:43
#: templates/devapp/dev.html:47 templates/devapp/fix_dev_group.html:51
#: templates/devapp/fix_dev_group.html:55
msgid "Find the device" msgid "Find the device"
msgstr "Найти устройство" msgstr "Найти устройство"
#: devapp/templates/devapp/add_dev.html:72 devapp/templates/devapp/dev.html:61
#: devapp/templates/devapp/fix_dev_group.html:64
#: devapp/templates/devapp/manage_ports/add_ports.html:75
#: devapp/templates/devapp/manage_ports/add_ports.html:79
#: devapp/templates/devapp/manage_ports/modal_add_edit_port.html:21
#: templates/devapp/add_dev.html:72 templates/devapp/dev.html:61
#: templates/devapp/fix_dev_group.html:64
#: templates/devapp/manage_ports/add_ports.html:75
#: templates/devapp/manage_ports/add_ports.html:79
#: templates/devapp/manage_ports/modal_add_edit_port.html:21
msgid "Save" msgid "Save"
msgstr "Сохранить" msgstr "Сохранить"
#: devapp/templates/devapp/add_dev.html:75 devapp/templates/devapp/dev.html:73
#: devapp/templates/devapp/fix_dev_group.html:67
#: templates/devapp/add_dev.html:75 templates/devapp/dev.html:73
#: templates/devapp/fix_dev_group.html:67
msgid "Reset" msgid "Reset"
msgstr "Сбросить" msgstr "Сбросить"
#: devapp/templates/devapp/custom_dev_page/olt.html:10
#: devapp/templates/devapp/custom_dev_page/onu.html:12
#: devapp/templates/devapp/custom_dev_page/ports.html:12
#: templates/devapp/custom_dev_page/olt.html:10
#: templates/devapp/custom_dev_page/olt_ztec320.html:11
#: templates/devapp/custom_dev_page/onu.html:12
#: templates/devapp/custom_dev_page/onu_for_zte.html:12
#: templates/devapp/custom_dev_page/ports.html:12
msgid "Uptime" msgid "Uptime"
msgstr "Без перезагрузки" msgstr "Без перезагрузки"
#: devapp/templates/devapp/custom_dev_page/olt.html:17
#: templates/devapp/custom_dev_page/olt.html:17
msgid "SNMP Num" msgid "SNMP Num"
msgstr "SNMP Ном." msgstr "SNMP Ном."
#: devapp/templates/devapp/custom_dev_page/olt.html:18
#: templates/devapp/custom_dev_page/olt.html:18
msgid "Name" msgid "Name"
msgstr "Имя" msgstr "Имя"
#: devapp/templates/devapp/custom_dev_page/olt.html:19
#: devapp/templates/devapp/custom_dev_page/onu.html:19
#: templates/devapp/custom_dev_page/olt.html:19
#: templates/devapp/custom_dev_page/onu.html:19
#: templates/devapp/custom_dev_page/onu_for_zte.html:19
msgid "Mac" msgid "Mac"
msgstr "Мак" msgstr "Мак"
#: devapp/templates/devapp/custom_dev_page/olt.html:20
#: devapp/templates/devapp/custom_dev_page/onu.html:73
#: templates/devapp/custom_dev_page/olt.html:20
#: templates/devapp/custom_dev_page/onu.html:74
#: templates/devapp/custom_dev_page/onu_for_zte.html:77
msgid "Signal" msgid "Signal"
msgstr "Ур. сигнала" msgstr "Ур. сигнала"
#: devapp/templates/devapp/custom_dev_page/olt.html:38
#: templates/devapp/custom_dev_page/olt.html:38
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:45
msgid "Create device" msgid "Create device"
msgstr "Создать устройство" msgstr "Создать устройство"
#: devapp/templates/devapp/custom_dev_page/olt.html:45
#: devapp/templates/devapp/manage_ports/list.html:48
#: templates/devapp/custom_dev_page/olt.html:45
#: templates/devapp/manage_ports/list.html:48
msgid "Ports not found" msgid "Ports not found"
msgstr "Порты не найдены" msgstr "Порты не найдены"
#: devapp/templates/devapp/custom_dev_page/onu.html:10
#: devapp/templates/devapp/custom_dev_page/ports.html:10
#: templates/devapp/custom_dev_page/olt_ztec320.html:31
#: templates/devapp/custom_dev_page/ports.html:54
msgid "We have not received info, please check options :("
msgstr "Инфа не получена, проверьте настройки :("
#: templates/devapp/custom_dev_page/olt_ztec320.html:36
msgid "Long description"
msgstr "Длинное описание"
#: templates/devapp/custom_dev_page/olt_ztec320.html:37
msgid "Hostname"
msgstr "Имя хоста"
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:15
msgid "OLT Scan"
msgstr "Скан OLT"
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:27
msgid "Onu type"
msgstr "Тип ONU"
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:28
msgid "Onu port"
msgstr "Порт ONU"
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:29
msgid "Onu signal"
msgstr "Сигнала ONU"
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:30
msgid "Serial"
msgstr "Серийный номер"
#: templates/devapp/custom_dev_page/olt_ztec320_ports.html:52
msgid "ONU not found"
msgstr "ONU не найдена"
#: templates/devapp/custom_dev_page/onu.html:10
#: templates/devapp/custom_dev_page/onu_for_zte.html:10
#: templates/devapp/custom_dev_page/ports.html:10
msgid "Title of the type of switch" msgid "Title of the type of switch"
msgstr "Название типа свича" msgstr "Название типа свича"
#: devapp/templates/devapp/custom_dev_page/onu.html:22
#: templates/devapp/custom_dev_page/onu.html:22
#: templates/devapp/custom_dev_page/onu_for_zte.html:22
msgid "Attached user" msgid "Attached user"
msgstr "Прикрепленный абонент" msgstr "Прикрепленный абонент"
#: devapp/templates/devapp/custom_dev_page/onu.html:47
#: templates/devapp/custom_dev_page/onu.html:48
#: templates/devapp/custom_dev_page/onu_for_zte.html:50
msgid "ONU Status" msgid "ONU Status"
msgstr "Состояние ONU" msgstr "Состояние ONU"
#: devapp/templates/devapp/custom_dev_page/onu.html:57
#: templates/devapp/custom_dev_page/onu.html:58
#: templates/devapp/custom_dev_page/onu_for_zte.html:60
msgid "ONU error" msgid "ONU error"
msgstr "ONU ошибка" msgstr "ONU ошибка"
#: devapp/templates/devapp/custom_dev_page/onu.html:71
#: templates/devapp/custom_dev_page/onu.html:72
#: templates/devapp/custom_dev_page/onu_for_zte.html:75
msgid "Name on OLT" msgid "Name on OLT"
msgstr "Имя на OLT" msgstr "Имя на OLT"
#: devapp/templates/devapp/custom_dev_page/onu.html:72
#: templates/devapp/custom_dev_page/onu.html:73
#: templates/devapp/custom_dev_page/onu_for_zte.html:76
msgid "Distance(m)" msgid "Distance(m)"
msgstr "Расстояние (м)" msgstr "Расстояние (м)"
#: devapp/templates/devapp/custom_dev_page/onu.html:77
#: devapp/templates/devapp/custom_dev_page/onu.html:85
#: templates/devapp/custom_dev_page/onu.html:78
#: templates/devapp/custom_dev_page/onu.html:86
msgid "Mac on OLT" msgid "Mac on OLT"
msgstr "MAC адрес на OLT" msgstr "MAC адрес на OLT"
#: devapp/templates/devapp/custom_dev_page/onu.html:78
#: templates/devapp/custom_dev_page/onu.html:79
msgid "Mac-addresses does not match" msgid "Mac-addresses does not match"
msgstr "MAC адреса не совпадают" msgstr "MAC адреса не совпадают"
#: devapp/templates/devapp/custom_dev_page/onu.html:79
#: devapp/templates/devapp/custom_dev_page/onu.html:81
#: templates/devapp/custom_dev_page/onu.html:80
#: templates/devapp/custom_dev_page/onu.html:82
msgid "Fix it" msgid "Fix it"
msgstr "Исправить" msgstr "Исправить"
#: devapp/templates/devapp/custom_dev_page/onu.html:92
#: templates/devapp/custom_dev_page/onu.html:93
#: templates/devapp/custom_dev_page/onu_for_zte.html:83
msgid "Info does not fetch" msgid "Info does not fetch"
msgstr "Информация не получена" msgstr "Информация не получена"
#: devapp/templates/devapp/custom_dev_page/ports.html:44
#: templates/devapp/custom_dev_page/ports.html:44
msgid "Disable port" msgid "Disable port"
msgstr "Выключить порт" msgstr "Выключить порт"
#: devapp/templates/devapp/custom_dev_page/ports.html:48
#: templates/devapp/custom_dev_page/ports.html:48
msgid "Enable port" msgid "Enable port"
msgstr "Включить порт" msgstr "Включить порт"
#: devapp/templates/devapp/custom_dev_page/ports.html:54
msgid "We have not received info, please check options :("
msgstr "Инфа не получена, проверьте настройки :("
#: devapp/templates/devapp/custom_dev_page/ports.html:80
#: templates/devapp/custom_dev_page/ports.html:80
msgid "Device log" msgid "Device log"
msgstr "Лог устройства" msgstr "Лог устройства"
#: devapp/templates/devapp/custom_dev_page/ports.html:87
#: templates/devapp/custom_dev_page/ports.html:87
msgid "Level" msgid "Level"
msgstr "Уровень" msgstr "Уровень"
#: devapp/templates/devapp/custom_dev_page/ports.html:89
#: templates/devapp/custom_dev_page/ports.html:89
msgid "Date" msgid "Date"
msgstr "Дата" msgstr "Дата"
#: devapp/templates/devapp/custom_dev_page/ports.html:104
#: templates/devapp/custom_dev_page/ports.html:104
msgid "Ports comment" msgid "Ports comment"
msgstr "Комментарии портов" msgstr "Комментарии портов"
#: devapp/templates/devapp/custom_dev_page/ports.html:111
#: templates/devapp/custom_dev_page/ports.html:111
msgid "Title" msgid "Title"
msgstr "Название" msgstr "Название"
#: devapp/templates/devapp/custom_dev_page/ports.html:122
#: templates/devapp/custom_dev_page/ports.html:122
msgid "We have not received info for ports" msgid "We have not received info for ports"
msgstr "Инфа о портах не получена" msgstr "Инфа о портах не получена"
#: devapp/templates/devapp/dev.html:65 devapp/templates/devapp/dev.html:69
#: devapp/templates/devapp/manage_ports/add_ports.html:53
#: devapp/templates/devapp/manage_ports/add_ports.html:55
#: devapp/templates/devapp/manage_ports/list.html:35
#: devapp/templates/devapp/manage_ports/modal_del_port.html:14
#: templates/devapp/dev.html:65 templates/devapp/dev.html:69
#: templates/devapp/manage_ports/add_ports.html:53
#: templates/devapp/manage_ports/add_ports.html:55
#: templates/devapp/manage_ports/list.html:35
#: templates/devapp/manage_ports/modal_del_port.html:14
msgid "Delete" msgid "Delete"
msgstr "Удалить" msgstr "Удалить"
#: devapp/templates/devapp/dev.html:68
#: templates/devapp/dev.html:68
msgid "Permission denied" msgid "Permission denied"
msgstr "Доступ запрещён" msgstr "Доступ запрещён"
#: devapp/templates/devapp/devices.html:81
#: devapp/templates/devapp/devices_null_group.html:62
#: templates/devapp/device_confirm_delete.html:9
msgid "Remove device"
msgstr "Удалить устройство"
#: templates/devapp/device_confirm_delete.html:13
msgid "Are you sure you want to delete device?"
msgstr "Вы уверены что хотите удалить устройство?"
#: templates/devapp/devices.html:81 templates/devapp/devices_null_group.html:62
msgid "Devices does not found" msgid "Devices does not found"
msgstr "Нет созданных устройств" msgstr "Нет созданных устройств"
#: devapp/templates/devapp/devices.html:81
#: devapp/templates/devapp/devices.html:91
#: devapp/templates/devapp/devices_null_group.html:62
#: devapp/templates/devapp/devices_null_group.html:72
#: templates/devapp/devices.html:81 templates/devapp/devices.html:91
#: templates/devapp/devices_null_group.html:62
#: templates/devapp/devices_null_group.html:72
msgid "Create" msgid "Create"
msgstr "Cоздать" msgstr "Cоздать"
#: devapp/templates/devapp/devices_null_group.html:13
#: devapp/templates/devapp/group_list.html:32
#: templates/devapp/devices_null_group.html:13
#: templates/devapp/group_list.html:32
msgid "Devices without group" msgid "Devices without group"
msgstr "Устройства без группы" msgstr "Устройства без группы"
#: devapp/templates/devapp/fix_dev_group.html:17
#: templates/devapp/fix_dev_group.html:17
msgid "Fix device group" msgid "Fix device group"
msgstr "Поправить группу устройства" msgstr "Поправить группу устройства"
#: devapp/templates/devapp/group_list.html:14
#: templates/devapp/group_list.html:14
msgid "Group title" msgid "Group title"
msgstr "Название" msgstr "Название"
#: devapp/templates/devapp/group_list.html:24
#: templates/devapp/group_list.html:24
msgid "Groups was not found" msgid "Groups was not found"
msgstr "Эта группа не найдена" msgstr "Эта группа не найдена"
#: devapp/templates/devapp/group_list.html:35
#: templates/devapp/group_list.html:35
msgid "Export to nagios objects" msgid "Export to nagios objects"
msgstr "Экспортировать конфиг для nagios" msgstr "Экспортировать конфиг для nagios"
#: devapp/templates/devapp/manage_ports/add_ports.html:10
#: devapp/templates/devapp/manage_ports/list.html:58
#: templates/devapp/manage_ports/add_ports.html:10
#: templates/devapp/manage_ports/list.html:58
msgid "Add ports" msgid "Add ports"
msgstr "Добавить порты" msgstr "Добавить порты"
#: devapp/templates/devapp/manage_ports/add_ports.html:32
#: templates/devapp/manage_ports/add_ports.html:32
msgid "Mode" msgid "Mode"
msgstr "Режим" msgstr "Режим"
#: devapp/templates/devapp/manage_ports/add_ports.html:60
#: devapp/templates/devapp/manage_ports/list.html:57
#: templates/devapp/manage_ports/add_ports.html:60
#: templates/devapp/manage_ports/list.html:57
msgid "Add" msgid "Add"
msgstr "Добавить" msgstr "Добавить"
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:10
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:16
#: templates/devapp/manage_ports/fix_abon_device.html:10
#: templates/devapp/manage_ports/fix_abon_device.html:16
msgid "Fix subscriber ports conflict" msgid "Fix subscriber ports conflict"
msgstr "Исправить конфликт абонентов на порту" msgstr "Исправить конфликт абонентов на порту"
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:21
#: templates/devapp/manage_ports/fix_abon_device.html:21
msgid "Subscribers list on port" msgid "Subscribers list on port"
msgstr "Список абонентов на порту" msgstr "Список абонентов на порту"
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:25
#: templates/devapp/manage_ports/fix_abon_device.html:25
msgid "" msgid ""
"You may choose the subscriber who correctly attached to device port. When " "You may choose the subscriber who correctly attached to device port. When "
"you have found right subscriber, remove the port from the other person" "you have found right subscriber, remove the port from the other person"
@ -397,15 +476,15 @@ msgstr ""
"найдёте нужного абонента удалите этот порт у другого абонента чтоб исправить " "найдёте нужного абонента удалите этот порт у другого абонента чтоб исправить "
"конфликт" "конфликт"
#: devapp/templates/devapp/manage_ports/fix_abon_device.html:31
#: templates/devapp/manage_ports/fix_abon_device.html:31
msgid "Abons not found" msgid "Abons not found"
msgstr "Абоненты не найдены" msgstr "Абоненты не найдены"
#: devapp/templates/devapp/manage_ports/list.html:13
#: templates/devapp/manage_ports/list.html:13
msgid "Count of subscribers" msgid "Count of subscribers"
msgstr "Сколько абонов" msgstr "Сколько абонов"
#: devapp/templates/devapp/manage_ports/list.html:26
#: templates/devapp/manage_ports/list.html:26
#, python-format #, python-format
msgid "" msgid ""
"Port should not have more than one subscriber, <a href='%(furl)s'>fix that</" "Port should not have more than one subscriber, <a href='%(furl)s'>fix that</"
@ -414,113 +493,105 @@ msgstr ""
"Порт не должен иметь больше одного назначенного абонента, <a " "Порт не должен иметь больше одного назначенного абонента, <a "
"href='%(furl)s'>исправить</a>" "href='%(furl)s'>исправить</a>"
#: devapp/templates/devapp/manage_ports/list.html:31
#: templates/devapp/manage_ports/list.html:31
msgid "Show subscriber on port" msgid "Show subscriber on port"
msgstr "Показать абонента на порту" msgstr "Показать абонента на порту"
#: devapp/templates/devapp/manage_ports/list.html:40
#: devapp/templates/devapp/manage_ports/modal_add_edit_port.html:14
#: templates/devapp/manage_ports/list.html:40
#: templates/devapp/manage_ports/modal_add_edit_port.html:14
msgid "Edit" msgid "Edit"
msgstr "Редактировать" msgstr "Редактировать"
#: devapp/templates/devapp/manage_ports/modal_del_port.html:7
#: templates/devapp/manage_ports/modal_del_port.html:7
msgid "Are you sure?" msgid "Are you sure?"
msgstr "Вы уверены?" msgstr "Вы уверены?"
#: devapp/templates/devapp/manage_ports/modal_del_port.html:11
#: templates/devapp/manage_ports/modal_del_port.html:11
msgid "Are you sure that you want to delete switch port from db?" msgid "Are you sure that you want to delete switch port from db?"
msgstr "Вы уверены что хотите удалить порт свича из бд?" msgstr "Вы уверены что хотите удалить порт свича из бд?"
#: devapp/templates/devapp/manage_ports/modal_show_subscriber_on_port.html:5
#: templates/devapp/manage_ports/modal_show_subscriber_on_port.html:5
msgid "Subscriber on port" msgid "Subscriber on port"
msgstr "Абонент на порту" msgstr "Абонент на порту"
#: devapp/views.py:80
msgid "Delete failed"
msgstr "Неизвестная ошибка при удалении :("
#: views.py:87
msgid "Device successfully deleted"
msgstr "Устройство успешно удалено"
#: devapp/views.py:109
#: views.py:115
msgid "You have redirected to existing device" msgid "You have redirected to existing device"
msgstr "Вы были переадресованы на существующее устройство" msgstr "Вы были переадресованы на существующее устройство"
#: devapp/views.py:112 devapp/views.py:361 devapp/views.py:457
#: views.py:118 views.py:368 views.py:481
msgid "Please attach group for device" msgid "Please attach group for device"
msgstr "Пожалуйста назначте устройству группу в настройках" msgstr "Пожалуйста назначте устройству группу в настройках"
#: devapp/views.py:121
#: views.py:127
msgid "Device info has been saved" msgid "Device info has been saved"
msgstr "Инфа о точке сохранена" msgstr "Инфа о точке сохранена"
#: devapp/views.py:124 devapp/views.py:307 devapp/views.py:336
#: devapp/views.py:459
#: views.py:130 views.py:314 views.py:343 views.py:483
msgid "Form is invalid, check fields and try again" msgid "Form is invalid, check fields and try again"
msgstr "Ошибка в данных, проверте их ещё раз" msgstr "Ошибка в данных, проверте их ещё раз"
#: devapp/views.py:127
#: views.py:133
#, python-format #, python-format
msgid "Duplicate user and port: %s" msgid "Duplicate user and port: %s"
msgstr "Пользователь с таким портом и устройством уже есть: %s" msgstr "Пользователь с таким портом и устройством уже есть: %s"
#: devapp/views.py:168 devapp/views.py:230
msgid "Device does not have a group, please fix that"
msgstr "У устройства нет группы, пожалуйста, исправьте это"
#: devapp/views.py:173 devapp/views.py:263 devapp/views.py:348
#: views.py:180 views.py:270 views.py:355
msgid "Device does not exist" msgid "Device does not exist"
msgstr "Устойство не найдено" msgstr "Устойство не найдено"
#: devapp/views.py:194
#: views.py:201
msgid "Subscribers on port does not exist" msgid "Subscribers on port does not exist"
msgstr "Абоненты на порту не найдены" msgstr "Абоненты на порту не найдены"
#: devapp/views.py:196
#: views.py:203
msgid "More than one subscriber on device port" msgid "More than one subscriber on device port"
msgstr "Больше одного абонента на порту устройства" msgstr "Больше одного абонента на порту устройства"
#: devapp/views.py:282
#: views.py:289
msgid "Port successfully removed" msgid "Port successfully removed"
msgstr "Порт успешно удалён" msgstr "Порт успешно удалён"
#: devapp/views.py:290 devapp/views.py:318
#: views.py:297 views.py:325
msgid "Port does not exist" msgid "Port does not exist"
msgstr "Порт не найден" msgstr "Порт не найден"
#: devapp/views.py:305 devapp/views.py:333
#: views.py:312 views.py:340
msgid "Port successfully saved" msgid "Port successfully saved"
msgstr "Порт успешно сохранён" msgstr "Порт успешно сохранён"
#: devapp/views.py:368 devapp/views.py:411
#: views.py:375 views.py:431
msgid "Dot was not pinged" msgid "Dot was not pinged"
msgstr "Эта точка не пингуется" msgstr "Эта точка не пингуется"
#: devapp/views.py:377 devapp/views.py:409
#: views.py:384 views.py:429
msgid "Not Set snmp device password" msgid "Not Set snmp device password"
msgstr "Не указан snmp пароль для устройства" msgstr "Не указан snmp пароль для устройства"
#: devapp/views.py:385
#: views.py:392
msgid "SNMP error on device" msgid "SNMP error on device"
msgstr "Ошибка SNMP на устройстве" msgstr "Ошибка SNMP на устройстве"
#: devapp/views.py:454
#: views.py:478
msgid "Device fixed" msgid "Device fixed"
msgstr "Устройство исправлено" msgstr "Устройство исправлено"
#: devapp/views.py:483
#: views.py:508
#, python-format #, python-format
msgid "Device with mac address %(mac)s does not exist" msgid "Device with mac address %(mac)s does not exist"
msgstr "Устройство мак адресом %(mac)s не найдено" msgstr "Устройство мак адресом %(mac)s не найдено"
#: devapp/views.py:491
#: views.py:516
msgid "Fixed" msgid "Fixed"
msgstr "Исправлено, обновите страницу" msgstr "Исправлено, обновите страницу"
#: devapp/views.py:494
#: views.py:519
msgid "Parent device not found" msgid "Parent device not found"
msgstr "Вышестоящее устройство не найдено" msgstr "Вышестоящее устройство не найдено"
#~ msgid "Plugin output"
#~ msgstr "Вывод мониторинга"
msgid "Device %(device_name)s is up" msgid "Device %(device_name)s is up"
msgstr "%(device_name)s в сети" msgstr "%(device_name)s в сети"
@ -532,6 +603,3 @@ msgstr "%(device_name)s недостижим"
msgid "Device %(device_name)s getting undefined status code" msgid "Device %(device_name)s getting undefined status code"
msgstr "Устройство %(device_name)s получило не определённый код состояния" msgstr "Устройство %(device_name)s получило не определённый код состояния"
msgid "Ip address or parent device with ip address required for ONU device"
msgstr "Необходим ip адрес. Или родительское устройство с назначенным ip адресом необходимо."

4
devapp/migrations/0001_initial.py

@ -5,7 +5,7 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import djing.fields import djing.fields
import mydefs
from djing.lib import MyGenericIPAddressField
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -21,7 +21,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('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, ('mac_addr', djing.fields.MACAddressField(blank=True, integer=True, null=True, unique=True,
verbose_name='Mac address')), verbose_name='Mac address')),
('comment', models.CharField(max_length=256, verbose_name='Comment')), ('comment', models.CharField(max_length=256, verbose_name='Comment')),

4
devapp/migrations/0002_auto_20180409_1318.py

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
import mydefs
from djing.lib import MyGenericIPAddressField
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -20,7 +20,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='device', model_name='device',
name='ip_address', 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'), verbose_name='Ip address'),
), ),
] ]

60
devapp/migrations/0003_auto_20180529_1311.py

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-05-29 13:11
from __future__ import unicode_literals
import os
from json import load
from django.db import migrations, models
from django.core import serializers
TMP_FILE = '/tmp/djing_snmp_info_backup.json'
def snmp_backup_info(apps, _):
Device = apps.get_model('devapp', 'Device')
obs = Device.objects.only('snmp_item_num')
with open(TMP_FILE, 'w') as f:
serializers.serialize('json', obs, stream=f)
def snmp_restore_info_to_new_scheme(apps, _):
Device = apps.get_model('devapp', 'Device')
with open(TMP_FILE, 'r') as f:
for device in load(f):
Device.objects.filter(pk=device['pk']).update(snmp_extra=device['fields']['snmp_item_num'])
if os.path.isfile(TMP_FILE):
os.remove(TMP_FILE)
class Migration(migrations.Migration):
dependencies = [
('devapp', '0002_auto_20180409_1318'),
]
operations = [
migrations.RunPython(snmp_backup_info),
migrations.AlterModelOptions(
name='device',
options={'ordering': ('id',), 'permissions': (('can_view_device', 'Can view device'),), 'verbose_name': 'Device', 'verbose_name_plural': 'Devices'},
),
migrations.AlterModelOptions(
name='port',
options={'ordering': ('num',), 'permissions': (('can_toggle_ports', 'Can toggle ports'),), 'verbose_name': 'Port', 'verbose_name_plural': 'Ports'},
),
migrations.RemoveField(
model_name='device',
name='snmp_item_num',
),
migrations.AddField(
model_name='device',
name='snmp_extra',
field=models.CharField(blank=True, max_length=256, null=True, verbose_name='SNMP extra info'),
),
migrations.AlterField(
model_name='device',
name='devtype',
field=models.CharField(choices=[('Dl', 'DLink switch'), ('Pn', 'PON OLT'), ('On', 'PON ONU'), ('Ex', 'Eltex switch'), ('Zt', 'OLT ZTE C320'), ('Zo', 'ZTE PON ONU')], default='Dl', max_length=2, verbose_name='Device type'),
),
migrations.RunPython(snmp_restore_info_to_new_scheme)
]

17
devapp/models.py

@ -1,8 +1,9 @@
import os import os
from django.db import models from django.db import models
from djing.fields import MACAddressField from djing.fields import MACAddressField
from .base_intr import DevBase from .base_intr import DevBase
from mydefs import MyGenericIPAddressField, MyChoicesAdapter
from djing.lib import MyGenericIPAddressField, MyChoicesAdapter
from . import dev_types from . import dev_types
from subprocess import run from subprocess import run
from django.conf import settings from django.conf import settings
@ -19,6 +20,8 @@ class DeviceMonitoringException(Exception):
class Device(models.Model): class Device(models.Model):
_cached_manager = None
ip_address = MyGenericIPAddressField(verbose_name=_('Ip address'), null=True, blank=True) ip_address = MyGenericIPAddressField(verbose_name=_('Ip address'), null=True, blank=True)
mac_addr = MACAddressField(verbose_name=_('Mac address'), null=True, blank=True, unique=True) mac_addr = MACAddressField(verbose_name=_('Mac address'), null=True, blank=True, unique=True)
comment = models.CharField(_('Comment'), max_length=256) comment = models.CharField(_('Comment'), max_length=256)
@ -27,7 +30,8 @@ class Device(models.Model):
('Pn', dev_types.OLTDevice), ('Pn', dev_types.OLTDevice),
('On', dev_types.OnuDevice), ('On', dev_types.OnuDevice),
('Ex', dev_types.EltexSwitch), ('Ex', dev_types.EltexSwitch),
('Zt', dev_types.Olt_ZTE_C320)
('Zt', dev_types.Olt_ZTE_C320),
('Zo', dev_types.ZteOnuDevice)
) )
devtype = models.CharField(_('Device type'), max_length=2, default=DEVICE_TYPES[0][0], devtype = models.CharField(_('Device type'), max_length=2, default=DEVICE_TYPES[0][0],
choices=MyChoicesAdapter(DEVICE_TYPES)) choices=MyChoicesAdapter(DEVICE_TYPES))
@ -36,7 +40,7 @@ class Device(models.Model):
parent_dev = models.ForeignKey('self', verbose_name=_('Parent device'), blank=True, null=True, parent_dev = models.ForeignKey('self', verbose_name=_('Parent device'), blank=True, null=True,
on_delete=models.SET_NULL) on_delete=models.SET_NULL)
snmp_item_num = models.PositiveSmallIntegerField(_('SNMP Number'), default=0, blank=True)
snmp_extra = models.CharField(_('SNMP extra info'), max_length=256, null=True, blank=True)
NETWORK_STATES = ( NETWORK_STATES = (
('und', _('Undefined')), ('und', _('Undefined')),
@ -69,11 +73,14 @@ class Device(models.Model):
res = klasses[0][1] res = klasses[0][1]
if issubclass(res, DevBase): if issubclass(res, DevBase):
return res return res
return
raise TypeError('one of types is not subclass of DevBase. '
'Or implementation of that device type is not found')
def get_manager_object(self) -> DevBase: def get_manager_object(self) -> DevBase:
man_klass = self.get_manager_klass() man_klass = self.get_manager_klass()
return man_klass(self)
if self._cached_manager is None:
self._cached_manager = man_klass(self)
return self._cached_manager
# Can attach device to subscriber in subscriber page # Can attach device to subscriber in subscriber page
def has_attachable_to_subscriber(self): def has_attachable_to_subscriber(self):

2
devapp/templates/devapp/add_dev.html

@ -63,7 +63,7 @@
</div> </div>
{% bootstrap_icon 'list-alt' as ic %} {% bootstrap_icon 'list-alt' as ic %}
{% bootstrap_field form.snmp_item_num addon_before=ic %}
{% bootstrap_field form.snmp_extra addon_before=ic %}
{% bootstrap_field form.is_noticeable %} {% bootstrap_field form.is_noticeable %}

2
devapp/templates/devapp/custom_dev_page/olt_ztec320_ports.html

@ -42,7 +42,7 @@
<td>{{ onu.onu_signal }}</td> <td>{{ onu.onu_signal }}</td>
<td>{{ onu.onu_sn }}</td> <td>{{ onu.onu_sn }}</td>
<td> <td>
<a href="{% url 'devapp:add' grp %}?t=On&pdev={{ dev.pk }}&c={{ onu.onu_sn }}" title="{% trans 'Create device' %}">
<a href="{% url 'devapp:add' grp %}?t=Zo&pdev={{ dev.pk }}&c={{ onu.onu_sn }}&n={{ onu.snmp_extra }}" title="{% trans 'Create device' %}">
<span class="glyphicon glyphicon-plus"></span> <span class="glyphicon glyphicon-plus"></span>
</a> </a>
</td> </td>

19
devapp/templates/devapp/custom_dev_page/onu.html

@ -31,9 +31,10 @@
{% if dev.parent_dev %} {% if dev.parent_dev %}
<li class="list-group-item"> <li class="list-group-item">
{% with pdev=dev.parent_dev pdgrp=dev.parent_dev.group %} {% with pdev=dev.parent_dev pdgrp=dev.parent_dev.group %}
{% trans 'Parent device' %}:<a href="{% url 'devapp:view' pdgrp.pk pdev.pk %}"
title="{{ pdev.mac_addr|default:'' }}"
target="_blank">{{ pdev.ip_address|default:'-' }} {{ pdev.comment }}</a>
{% trans 'Parent device' %}:
<a href="{% url 'devapp:view' pdgrp.pk pdev.pk %}" title="{{ pdev.mac_addr|default:'' }}" target="_blank">
{{ pdev.ip_address|default:'-' }} {{ pdev.comment }}
</a>
{% endwith %} {% endwith %}
</li> </li>
{% endif %} {% endif %}
@ -51,20 +52,20 @@
{% if onu_details %} {% if onu_details %}
{% if onu_details.err %} {% if onu_details.err %}
<div class="media"> <div class="media">
<div class="media-left"><span class="media-object glyphicon glyphicon-remove-sign text-danger" style="font-size: 75px;"></span></div>
<div class="media-left"><span class="media-object glyphicon glyphicon-remove-sign text-danger font-extra-large"></span></div>
</div> </div>
<div class="media-body"> <div class="media-body">
<b>{% trans 'ONU error' %}</b>: {{ onu_details.err }}<br> <b>{% trans 'ONU error' %}</b>: {{ onu_details.err }}<br>
</div> </div>
{% else %} {% else %}
<div class="media"> <div class="media">
<div class="media-left">
<div class="media-left font-extra-large">
{% if onu_details.status == '3' %} {% if onu_details.status == '3' %}
<span class="media-object glyphicon glyphicon-ok-sign text-success" style="font-size: 75px;"></span>
<span class="media-object glyphicon glyphicon-ok-sign text-success"></span>
{% elif onu_details.status == '2' %} {% elif onu_details.status == '2' %}
<span class="media-object glyphicon glyphicon-remove-sign text-danger" style="font-size: 75px;"></span>
<span class="media-object glyphicon glyphicon-remove-sign text-danger"></span>
{% else %} {% else %}
<span class="media-object glyphicon glyphicon-question-sign" style="font-size: 75px;"></span>
<span class="media-object glyphicon glyphicon-question-sign"></span>
{% endif %} {% endif %}
</div> </div>
<div class="media-body"> <div class="media-body">
@ -76,7 +77,7 @@
<span class="text-danger"> <span class="text-danger">
<b>{% trans 'Mac on OLT' %}</b>: {{ onu_details.mac }} <b>{% trans 'Mac on OLT' %}</b>: {{ onu_details.mac }}
<h4 class="glyphicon glyphicon-exclamation-sign" title="{% trans 'Mac-addresses does not match' %}" data-toggle="tooltip"></h4> <h4 class="glyphicon glyphicon-exclamation-sign" title="{% trans 'Mac-addresses does not match' %}" data-toggle="tooltip"></h4>
<a href="{% url 'devapp:fix_onu' %}" data-param="{{ dev.mac_addr }}" class="btn btn-default btn-xs btn-cmd" title="{% trans 'Fix it' %}" data-toggle="tooltip">
<a href="{% url 'devapp:fix_onu' %}" data-param="{{ dev.mac_addr }}" class="btn btn-default btn-xs btn-cmd" title="{% trans 'Fix it' %}" data-toggle="tooltip">
<span class="glyphicon glyphicon-fire"></span> <span class="glyphicon glyphicon-fire"></span>
<span class="hidden-xs">{% trans 'Fix it' %}</span> <span class="hidden-xs">{% trans 'Fix it' %}</span>
</a> </a>

91
devapp/templates/devapp/custom_dev_page/onu_for_zte.html

@ -0,0 +1,91 @@
{% extends request.is_ajax|yesno:'nullcont.htm,devapp/ext.htm' %}
{% load i18n %}
{% block content %}
{% with uptime=dev_manager.uptime onu_details=dev_manager.get_details %}
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-title">{{ dev.get_devtype_display|default:_('Title of the type of switch') }}.
{% if uptime %}
{% trans 'Uptime' %} {{ uptime }}
{% endif %}
</div>
</div>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item">{% trans 'Ip address' %}: {{ dev.ip_address|default:'-' }}</li>
<li class="list-group-item">{% trans 'Mac' %}: {{ dev.mac_addr }}</li>
<li class="list-group-item">{% trans 'Description' %}: {{ dev.comment }}</li>
{% for da in dev_accs %}
<li class="list-group-item">{% trans 'Attached user' %}:
{% if da.group %}
<a href="{% url 'abonapp:abon_home' da.group.pk da.username %}"
target="_blank">{{ da.get_full_name }}</a>
{% else %}
{{ da.get_full_name }}
{% endif %}
</li>
{% endfor %}
{% if dev.parent_dev %}
<li class="list-group-item">
{% with pdev=dev.parent_dev pdgrp=dev.parent_dev.group %}
{% trans 'Parent device' %}:
<a href="{% url 'devapp:view' pdgrp.pk pdev.pk %}"
title="{{ pdev.mac_addr|default:'' }}"
target="_blank">
{{ pdev.ip_address|default:'-' }} {{ pdev.comment }}
</a>
{% endwith %}
</li>
{% endif %}
</ul>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'ONU Status' %}</h3>
</div>
<div class="panel-body">
{% if onu_details %}
{% if onu_details.err %}
<div class="media">
<div class="media-left"><span class="media-object glyphicon glyphicon-remove-sign text-danger font-extra-large"></span></div>
</div>
<div class="media-body">
<b>{% trans 'ONU error' %}</b>: {{ onu_details.err }}<br>
</div>
{% else %}
<div class="media">
<div class="media-left font-extra-large">
{% if onu_details.status == '1' %}
<span class="media-object glyphicon glyphicon-ok-sign text-success"></span>
{% elif onu_details.status == '2' %}
<span class="media-object glyphicon glyphicon-remove-sign text-danger"></span>
{% else %}
<span class="media-object glyphicon glyphicon-question-sign"></span>
{% endif %}
</div>
<div class="media-body">
<b>{% trans 'Name on OLT' %}</b>: {{ onu_details.name }}<br>
<b>{% trans 'Distance(m)' %}</b>: {{ onu_details.distance }}<br>
<b>{% trans 'Signal' %}</b>: {{ onu_details.signal }}<br>
</div>
</div>
{% endif %}
{% else %}
<h3>{% trans 'Info does not fetch' %}</h3>
{% endif %}
</div>
</div>
</div>
</div>
{% endwith %}
{% endblock %}

4
devapp/templates/devapp/dev.html

@ -52,7 +52,7 @@
</div> </div>
{% bootstrap_icon 'list-alt' as ic %} {% bootstrap_icon 'list-alt' as ic %}
{% bootstrap_field form.snmp_item_num addon_before=ic %}
{% bootstrap_field form.snmp_extra addon_before=ic %}
{% bootstrap_field form.is_noticeable %} {% bootstrap_field form.is_noticeable %}
@ -61,7 +61,7 @@
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %} <span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button> </button>
{% if perms.devapp.delete_device %} {% if perms.devapp.delete_device %}
<a href="{% url 'devapp:del' group.pk|default:0 dev.pk %}" class="btn btn-danger">
<a href="{% url 'devapp:del' group.pk|default:0 dev.pk %}" class="btn btn-danger btn-modal">
<span class="glyphicon glyphicon-remove"></span> {% trans 'Delete' %} <span class="glyphicon glyphicon-remove"></span> {% trans 'Delete' %}
</a> </a>
{% else %} {% else %}

14
devapp/templates/devapp/device_confirm_delete.html

@ -0,0 +1,14 @@
{% extends 'base_delete_modal.html' %}
{% load i18n %}
{% block modal_form_url %}
{% url 'devapp:del' object.group.pk object.pk %}
{% endblock %}
{% block modal_form_title %}
{% trans 'Remove device' %}
{% endblock %}
{% block modal_form_text %}
<h4>{% trans 'Are you sure you want to delete device?' %}</h4>
{% endblock %}

4
devapp/templates/devapp/devices.html

@ -30,7 +30,6 @@
{% if order_by == 'comment' %}<span class="glyphicon glyphicon-filter"></span>{% endif %} {% if order_by == 'comment' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th> </th>
<th class="col-md-3">{% trans 'Mac address' %}</th> <th class="col-md-3">{% trans 'Mac address' %}</th>
{# <th class="col-md-3 hidden-xs hidden-sm">{% trans 'Plugin output' %}</th> #}
<th class="col-md-1"> <th class="col-md-1">
<a href="{% url 'devapp:devs' group.pk %}?{% url_replace request order_by='devtype' dir=dir|default:'down' %}"> <a href="{% url 'devapp:devs' group.pk %}?{% url_replace request order_by='devtype' dir=dir|default:'down' %}">
{% trans 'Device type' %} {% trans 'Device type' %}
@ -61,11 +60,10 @@
<td><a href="{% url 'devapp:view' grpid dev.pk %}">{{ dev.ip_address }}</a></td> <td><a href="{% url 'devapp:view' grpid dev.pk %}">{{ dev.ip_address }}</a></td>
<td>{{ dev.comment }}</td> <td>{{ dev.comment }}</td>
<td>{{ dev.mac_addr|default:_('Not assigned') }}</td> <td>{{ dev.mac_addr|default:_('Not assigned') }}</td>
{# <td class="hidden-xs hidden-sm">{{ dev.mon.plugin_output|default:'&ndash;' }}</td> #}
<td>{{ dev.get_devtype_display }}</td> <td>{{ dev.get_devtype_display }}</td>
<td class="btn-group btn-group-xs btn-group-justified"> <td class="btn-group btn-group-xs btn-group-justified">
{% if can_del_dev %} {% if can_del_dev %}
<a href="{% url 'devapp:del' grpid dev.pk %}" class="btn btn-danger">
<a href="{% url 'devapp:del' grpid dev.pk %}" class="btn btn-danger btn-modal">
<span class="glyphicon glyphicon-remove"></span> <span class="glyphicon glyphicon-remove"></span>
</a> </a>
{% endif %} {% endif %}

4
devapp/templates/devapp/devices_null_group.html

@ -46,12 +46,12 @@
<td>{{ dev.get_devtype_display }}</td> <td>{{ dev.get_devtype_display }}</td>
<td class="btn-group btn-group-sm"> <td class="btn-group btn-group-sm">
{% if can_del_dev %} {% if can_del_dev %}
<a href="{% url 'devapp:del' 0 dev.pk %}" class="btn btn-danger btn-sm">
<a href="{% url 'devapp:del' 0 dev.pk %}" class="btn btn-danger btn-modal">
<span class="glyphicon glyphicon-remove"></span> <span class="glyphicon glyphicon-remove"></span>
</a> </a>
{% endif %} {% endif %}
{% if can_change_dev %} {% if can_change_dev %}
<a href="{% url 'devapp:edit' 0 dev.pk %}" class="btn btn-default btn-sm">
<a href="{% url 'devapp:edit' 0 dev.pk %}" class="btn btn-default">
<span class="glyphicon glyphicon-edit"></span> <span class="glyphicon glyphicon-edit"></span>
</a> </a>
{% endif %} {% endif %}

2
devapp/urls.py

@ -10,7 +10,7 @@ urlpatterns = [
url(r'^(?P<group_id>\d+)$', views.DevicesListView.as_view(), name='devs'), url(r'^(?P<group_id>\d+)$', views.DevicesListView.as_view(), name='devs'),
url(r'^(?P<group_id>\d+)/add$', views.dev, name='add'), url(r'^(?P<group_id>\d+)/add$', views.dev, name='add'),
url(r'^(\d+)/(?P<device_id>\d+)$', views.devview, name='view'), url(r'^(\d+)/(?P<device_id>\d+)$', views.devview, name='view'),
url(r'^(\d+)/(?P<device_id>\d+)/del$', views.devdel, name='del'),
url(r'^(\d+)/(?P<device_id>\d+)/del$', views.DeviceVeleteView.as_view(), name='del'),
url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/add$', views.add_single_port, name='add_port'), url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/add$', views.add_single_port, name='add_port'),
url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/edit$', views.dev, name='edit'), url(r'^(?P<group_id>\d+)/(?P<device_id>\d+)/edit$', views.dev, name='edit'),
url(r'^(\d+)/(?P<device_id>\d+)/ports$', views.manage_ports, name='manage_ports'), url(r'^(\d+)/(?P<device_id>\d+)/ports$', views.manage_ports, name='manage_ports'),

49
devapp/views.py

@ -11,10 +11,11 @@ from django.contrib import messages
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _, gettext from django.utils.translation import gettext_lazy as _, gettext
from easysnmp import EasySNMPTimeoutError, EasySNMPError from easysnmp import EasySNMPTimeoutError, EasySNMPError
from django.views.generic import DetailView
from django.views.generic import DetailView, DeleteView
from devapp.base_intr import DeviceImplementationError from devapp.base_intr import DeviceImplementationError
from mydefs import res_success, res_error, only_admins, safe_int
from djing.lib.decorators import only_admins
from djing.lib import safe_int
from abonapp.models import Abon from abonapp.models import Abon
from group_app.models import Group from group_app.models import Group
from accounts_app.models import UserProfile from accounts_app.models import UserProfile
@ -68,19 +69,23 @@ class DevicesWithoutGroupsListView(global_base_views.OrderingMixin, BaseDeviceLi
queryset = Device.objects.filter(group=None).only('comment', 'devtype', 'pk', 'ip_address') queryset = Device.objects.filter(group=None).only('comment', 'devtype', 'pk', 'ip_address')
@login_required
@permission_required('devapp.delete_device')
def devdel(request, device_id):
try:
device_instance = Device.objects.get(pk=device_id)
back_url = resolve_url('devapp:devs', group_id=device_instance.group.pk if device_instance.group else 0)
device_instance.update_dhcp(remove=True)
device_instance.delete()
return res_success(request, back_url)
except Device.DoesNotExist:
return res_error(request, _('Delete failed'))
except DeviceDBException as e:
return res_error(request, e)
@method_decorator(login_required, name='dispatch')
@method_decorator(permission_required('devapp.delete_device'), name='dispatch')
class DeviceVeleteView(DeleteView):
model = Device
pk_url_kwarg = 'device_id'
def get_success_url(self):
return resolve_url('devapp:devs', group_id=self.object.group.pk if self.object.group else 0)
def delete(self, request, *args, **kwargs):
res = super().delete(request, *args, **kwargs)
try:
self.object.update_dhcp(remove=True)
except DeviceDBException as e:
messages.error(request, e)
messages.success(request, _('Device successfully deleted'))
return res
@login_required @login_required
@ -137,7 +142,7 @@ def dev(request, group_id, device_id=0):
'comment': request.GET.get('c'), 'comment': request.GET.get('c'),
'ip_address': request.GET.get('ip'), 'ip_address': request.GET.get('ip'),
'man_passw': getattr(settings, 'DEFAULT_SNMP_PASSWORD', ''), 'man_passw': getattr(settings, 'DEFAULT_SNMP_PASSWORD', ''),
'snmp_item_num': request.GET.get('n') or 0
'snmp_extra': request.GET.get('n') or ''
}) })
else: else:
frm = DeviceForm(instance=devinst) frm = DeviceForm(instance=devinst)
@ -505,8 +510,8 @@ def fix_onu(request):
# convert bytes mac address to str presentation mac address # convert bytes mac address to str presentation mac address
real_mac = ':'.join('%x' % ord(i) for i in srcmac) real_mac = ':'.join('%x' % ord(i) for i in srcmac)
if mac == real_mac: if mac == real_mac:
onu.snmp_item_num = snmpnum
onu.save(update_fields=('snmp_item_num',))
onu.snmp_extra = str(snmpnum)
onu.save(update_fields=('snmp_extra',))
status = 0 status = 0
text = '<span class="glyphicon glyphicon-ok"></span> <span class="hidden-xs">%s</span>' % _('Fixed') text = '<span class="glyphicon glyphicon-ok"></span> <span class="hidden-xs">%s</span>' % _('Fixed')
break break
@ -619,11 +624,11 @@ class NagiosObjectsConfView(global_base_views.AuthenticatedOrHashAuthView):
if device.devtype == 'On': if device.devtype == 'On':
if device.parent_dev: if device.parent_dev:
host_addr = device.parent_dev.ip_address host_addr = device.parent_dev.ip_address
conf = self.templ_onu(host_name, host_addr, mac=mac_addr, snmp_item=device.snmp_item_num or None)
conf = self.templ_onu(host_name, host_addr, mac=mac_addr, snmp_item=device.snmp_extra or None)
else: else:
if device.ip_address: if device.ip_address:
host_addr = device.ip_address host_addr = device.ip_address
conf = self.templ_onu(host_name, host_addr, mac=mac_addr, snmp_item=device.snmp_item_num or None)
conf = self.templ_onu(host_name, host_addr, mac=mac_addr, snmp_item=device.snmp_extra or None)
else: else:
parent_host_name = norm_name("%d%s" % ( parent_host_name = norm_name("%d%s" % (
device.parent_dev.pk, translit(device.parent_dev.comment, language_code='ru', reversed=True) device.parent_dev.pk, translit(device.parent_dev.comment, language_code='ru', reversed=True)
@ -651,7 +656,7 @@ class NagiosObjectsConfView(global_base_views.AuthenticatedOrHashAuthView):
return '\n'.join(i for i in r if i) return '\n'.join(i for i in r if i)
@staticmethod @staticmethod
def templ_onu(host_name: str, host_addr: str, mac: Optional[str], snmp_item: int):
def templ_onu(host_name: str, host_addr: str, mac: Optional[str], snmp_item: str):
if not host_addr: if not host_addr:
return return
r = ( r = (
@ -659,7 +664,7 @@ class NagiosObjectsConfView(global_base_views.AuthenticatedOrHashAuthView):
"\tuse device-onu", "\tuse device-onu",
"\thost_name %s" % host_name, "\thost_name %s" % host_name,
"\taddress %s" % host_addr, "\taddress %s" % host_addr,
"\t_snmp_item %d" % snmp_item if snmp_item is not None else '',
"\t_snmp_item %s" % snmp_item if snmp_item is not None else '',
"\t_mac_addr %s" % mac if mac is not None else '', "\t_mac_addr %s" % mac if mac is not None else '',
"}\n" "}\n"
) )

2
dialing.py

@ -9,7 +9,7 @@ from urllib.error import HTTPError
from hashlib import sha256 from hashlib import sha256
from pid.decorator import pidfile from pid.decorator import pidfile
from messaging.sms import SmsSubmit, SmsDeliver
from djing.lib.messaging.sms import SmsSubmit, SmsDeliver
from asterisk import manager as ast_mngr from asterisk import manager as ast_mngr

3
dialing_app/views.py

@ -16,7 +16,8 @@ from jsonview.decorators import json_view
from abonapp.models import Abon from abonapp.models import Abon
from djing.global_base_views import SecureApiView from djing.global_base_views import SecureApiView
from djing import JSONType from djing import JSONType
from mydefs import only_admins, safe_int
from djing.lib import safe_int
from djing.lib.decorators import only_admins
from .models import AsteriskCDR, SMSModel, SMSOut from .models import AsteriskCDR, SMSModel, SMSOut
from .forms import SMSOutForm from .forms import SMSOutForm

70
mydefs.py → djing/lib/__init__.py

@ -1,16 +1,9 @@
# -*- coding: utf-8 -*-
from datetime import timedelta
from json import dumps
import socket import socket
import struct import struct
from abc import ABCMeta
from datetime import timedelta
from collections import Iterator from collections import Iterator
from functools import wraps
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import redirect
from django.db import models from django.db import models
from django.conf import settings
DEBUG = getattr(settings, 'DEBUG', False)
def ip2int(addr): def ip2int(addr):
@ -41,20 +34,6 @@ def safe_int(i):
return 0 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): class MyGenericIPAddressField(models.GenericIPAddressField):
description = "Int32 notation ip address" description = "Int32 notation ip address"
@ -105,19 +84,7 @@ class MyChoicesAdapter(Iterator):
return res return res
# Декоратор проверяет аккаунт, чтоб не пускать клиентов в страницы администрации
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
# Русифицированный вывод timedelta
# Russian localized timedelta
class RuTimedelta(timedelta): class RuTimedelta(timedelta):
def __new__(cls, tm): def __new__(cls, tm):
if isinstance(tm, timedelta): if isinstance(tm, timedelta):
@ -148,23 +115,6 @@ class RuTimedelta(timedelta):
return 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): class MultipleException(Exception):
def __init__(self, err_list): def __init__(self, err_list):
if not isinstance(err_list, (list, tuple)): if not isinstance(err_list, (list, tuple)):
@ -176,12 +126,10 @@ class LogicError(Exception):
pass pass
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
class Singleton(type):
_instances = {}
return getinstance
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]

0
djing/auth_backends.py → djing/lib/auth_backends.py

35
djing/lib/decorators.py

@ -0,0 +1,35 @@
from functools import wraps
from django.conf import settings
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
DEBUG = getattr(settings, 'DEBUG', False)
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
# 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

0
messaging/__init__.py → djing/lib/messaging/__init__.py

7
djing/lib/messaging/sms/__init__.py

@ -0,0 +1,7 @@
# See LICENSE
from djing.lib.messaging.sms.deliver import SmsDeliver
from djing.lib.messaging.sms.submit import SmsSubmit
from djing.lib.messaging.sms.gsm0338 import is_gsm_text
__all__ = ("SmsSubmit", "SmsDeliver", "is_gsm_text")

0
messaging/sms/base.py → djing/lib/messaging/sms/base.py

0
messaging/sms/consts.py → djing/lib/messaging/sms/consts.py

10
messaging/sms/deliver.py → djing/lib/messaging/sms/deliver.py

@ -3,11 +3,11 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from messaging.utils import (swap, swap_number, encode_bytes, debug,
unpack_msg, unpack_msg2, to_array)
from messaging.sms import consts
from messaging.sms.base import SmsBase
from messaging.sms.udh import UserDataHeader
from djing.lib.messaging.utils import (swap, swap_number, encode_bytes, debug,
unpack_msg, unpack_msg2, to_array)
from djing.lib.messaging.sms import consts
from djing.lib.messaging.sms.submit import SmsBase
from djing.lib.messaging.sms.udh import UserDataHeader
class SmsDeliver(SmsBase): class SmsDeliver(SmsBase):

0
messaging/sms/gsm0338.py → djing/lib/messaging/sms/gsm0338.py

0
messaging/sms/pdu.py → djing/lib/messaging/sms/pdu.py

18
messaging/sms/submit.py → djing/lib/messaging/sms/submit.py

@ -4,15 +4,15 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import re import re
from messaging.sms import consts
from messaging.utils import (debug, encode_str, clean_number,
pack_8bits_to_ucs2, pack_8bits_to_7bits,
pack_8bits_to_8bit,
timedelta_to_relative_validity,
datetime_to_absolute_validity)
from messaging.sms.base import SmsBase
from messaging.sms.gsm0338 import is_gsm_text
from messaging.sms.pdu import Pdu
from djing.lib.messaging.sms import consts
from djing.lib.messaging.utils import (debug, encode_str, clean_number,
pack_8bits_to_ucs2, pack_8bits_to_7bits,
pack_8bits_to_8bit,
timedelta_to_relative_validity,
datetime_to_absolute_validity)
from djing.lib.messaging.sms.base import SmsBase
from djing.lib.messaging.sms.gsm0338 import is_gsm_text
from djing.lib.messaging.sms.pdu import Pdu
VALID_NUMBER = re.compile("^\+?\d{3,20}$") VALID_NUMBER = re.compile("^\+?\d{3,20}$")

0
messaging/sms/udh.py → djing/lib/messaging/sms/udh.py

2
messaging/sms/wap.py → djing/lib/messaging/sms/wap.py

@ -2,7 +2,7 @@
from array import array from array import array
from messaging.mms.mms_pdu import MMSDecoder
from djing.lib.messaging import MMSDecoder
def is_a_wap_push_notification(s): def is_a_wap_push_notification(s):

0
messaging/utils.py → djing/lib/messaging/utils.py

4
djing/lib/tln/__init__.py

@ -0,0 +1,4 @@
from .tln import *
__all__ = ('TelnetApi', 'ValidationError', 'ZTEFiberNumberNotFound', 'ZTEFiberIsFull',
'OnuZteRegisterError', 'ZteOltConsoleError', 'register_onu_ZTE_F660')

266
djing/lib/tln/tln.py

@ -0,0 +1,266 @@
#!/usr/bin/env python3
import re
import struct
from telnetlib import Telnet
from time import sleep
from typing import Generator, Dict, Optional, Tuple
class ZteOltConsoleError(Exception):
pass
class OnuZteRegisterError(ZteOltConsoleError):
pass
class ZTEFiberIsFull(ZteOltConsoleError):
pass
class ZTEFiberNumberNotFound(ZteOltConsoleError):
pass
class ValidationError(ValueError):
pass
MAC_ADDR_REGEX = b'^([0-9A-Fa-f]{1,2}[:-]){5}([0-9A-Fa-f]{1,2})$'
IP_ADDR_REGEX = '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' \
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' \
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' \
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
ONU_SN_REGEX = b'^ZTEG[A-F\d]{8}$'
class TelnetApi(Telnet):
config_level = []
def __init__(self, *args, **kwargs):
timeout = kwargs.get('timeout')
if timeout:
self._timeout = timeout
self._prompt_string = b'ZTE-C320-PKP#'
super().__init__(*args, **kwargs)
def write(self, buffer: bytes) -> None:
buffer = buffer + b'\n'
print('>>', buffer)
super().write(buffer)
def resize_screen(self, width: int, height: int):
naws_cmd = struct.pack('>BBBHHBB',
255, 250, 31, # IAC SB NAWS
width, height,
255, 240 # IAC SE
)
sock = self.get_socket()
sock.send(naws_cmd)
def enter(self, username: bytes, passw: bytes) -> None:
self.read_until(b'Username:')
self.write(username)
self.read_until(b'Password:')
self.write(passw)
def read_lines(self) -> Generator:
while True:
line = self.read_until(b'\r\n', timeout=self._timeout)
line = line.replace(b'\r\n', b'')
if self._prompt_string == line:
break
if line == b'':
continue
yield line
def command_to(self, cmd: bytes) -> Generator:
self.write(cmd)
return self.read_lines()
def set_prompt_string(self, prompt_string: bytes) -> None:
self.config_level.append(prompt_string)
self._prompt_string = prompt_string
def level_exit(self) -> Optional[Tuple]:
if len(self.config_level) < 2:
print('We are in root')
return
self.config_level.pop()
self.set_prompt_string(self.config_level[-1])
return tuple(self.command_to(b'exit'))
def __del__(self):
if self.sock:
self.write(b'exit')
super().__del__()
def parse_onu_name(onu_name: bytes, name_regexp=re.compile(b'[/:_]')) -> Dict[str, bytes]:
gpon_onu, stack_num, rack_num, fiber_num, onu_num = name_regexp.split(onu_name)
return {
'stack_num': stack_num,
'rack_num': rack_num,
'fiber_num': fiber_num,
'onu_num': onu_num
}
class OltZTERegister(TelnetApi):
def __init__(self, screen_size: Tuple[int, int], *args, **kwargs):
super().__init__(*args, **kwargs)
self.resize_screen(*screen_size)
def get_unregistered_onu(self, sn: bytes) -> Optional[Dict]:
lines = tuple(self.command_to(b'show gpon onu uncfg'))
if len(lines) > 3:
# devices available
# find onu by sn
line = tuple(ln for ln in lines if sn.lower() in ln.lower())
if len(line) > 0:
line = line[0]
onu_name, onu_sn, onu_state = line.split()
onu_numbers = parse_onu_name(onu_name)
onu_numbers.update({
'onu_name': onu_name,
'onu_sn': onu_sn,
'onu_state': onu_state
})
return onu_numbers
def get_last_registered_onu_number(self, stack_num: int, rack_num: int, fiber_num: int) -> int:
registered_lines = self.command_to(b'show run int gpon-olt_%d/%d/%d' % (
stack_num,
rack_num,
fiber_num
))
onu_type_regexp = re.compile(b'^\s{2}onu \d{1,3} type [-\w\d]{4,64} sn \w{4,64}$')
last_onu = 0
for rl in registered_lines:
if rl == b' --More--':
self.write(b' ')
if onu_type_regexp.match(rl):
_onu, num, _type, onu_type, _sn, onu_sn = rl.split()
last_onu = int(num)
return last_onu
def enter_to_config_mode(self) -> bool:
prompt = b'ZTE-C320-PKP(config)#'
self.set_prompt_string(prompt)
res = tuple(self.command_to(b'config terminal'))
if res[1].startswith(b'Enter configuration commands'):
# ok, we in the config mode
return True
return False
def go_to_olt_interface(self, stack_num: int, rack_num: int, fiber_num: int) -> Tuple:
self.set_prompt_string(b'ZTE-C320-PKP(config-if)#')
return tuple(self.command_to(b'interface gpon-olt_%d/%d/%d' % (
stack_num,
rack_num,
fiber_num
)))
def go_to_onu_interface(self, stack_num: int, rack_num: int, fiber_num: int, onu_port_num: int) -> Tuple:
self.set_prompt_string(b'ZTE-C320-PKP(config-if)#')
return tuple(self.command_to(b'interface gpon-onu_%d/%d/%d:%d' % (
stack_num,
rack_num,
fiber_num,
onu_port_num
)))
def apply_conf_to_onu(self, mac_addr: bytes, vlan_id: int) -> None:
tmpl = (
b'switchport vlan %d tag vport 1' % vlan_id,
b'port-location format flexible-syntax vport 1',
b'port-location sub-option remote-id enable vport 1',
b'port-location sub-option remote-id name %s vport 1' % mac_addr,
b'dhcp-option82 enable vport 1',
b'dhcp-option82 trust true replace vport 1',
b'ip dhcp snooping enable vport 1'
)
for conf_line in tmpl:
self.write(conf_line)
def register_onu_on_olt_fiber(self, onu_type: bytes, new_onu_num: int, onu_sn: bytes, line_profile: bytes,
remote_profile: bytes) -> Tuple:
# ok, we in interface
tpl = b'onu %d type %s sn %s' % (new_onu_num, onu_type, onu_sn)
r = tuple(self.command_to(tpl))
return tuple(self.command_to(b'onu %d profile line %s remote %s' % (
new_onu_num,
line_profile,
remote_profile
))) + r
def register_onu_ZTE_F660(olt_ip: str, onu_sn: bytes, login_passwd: Tuple[bytes, bytes], onu_mac: bytes):
onu_type = b'ZTE-F660'
line_profile = b'ZTE-F660-LINE'
remote_profile = b'ZTE-F660-ROUTER'
if not re.match(MAC_ADDR_REGEX, onu_mac):
raise ValidationError
if not re.match(IP_ADDR_REGEX, olt_ip):
raise ValidationError
if not re.match(ONU_SN_REGEX, onu_sn):
raise ValidationError
tn = OltZTERegister(host=olt_ip, timeout=2, screen_size=(120, 128))
tn.enter(*login_passwd)
unregistered_onu = tn.get_unregistered_onu(onu_sn)
if unregistered_onu is None:
raise OnuZteRegisterError('unregistered onu not found, sn=%s' % onu_sn.decode('utf-8'))
stack_num = int(unregistered_onu['stack_num'])
rack_num = int(unregistered_onu['rack_num'])
fiber_num = int(unregistered_onu['fiber_num'])
last_onu_number = tn.get_last_registered_onu_number(
stack_num, rack_num, fiber_num
)
if last_onu_number < 1:
raise ZTEFiberNumberNotFound
elif last_onu_number > 126:
raise ZTEFiberIsFull('olt fiber %d is full' % fiber_num)
# enter to config
if not tn.enter_to_config_mode():
raise ZteOltConsoleError('Failed to enter to config mode')
# go to olt interface
if not tn.go_to_olt_interface(stack_num, rack_num, fiber_num):
raise ZteOltConsoleError('Failed to enter in olt fiber port')
# new onu port number
new_onu_port_num = last_onu_number + 1
# register onu on olt interface
r = tn.register_onu_on_olt_fiber(onu_type, new_onu_port_num, onu_sn, line_profile, remote_profile)
print(r)
# exit from olt interface
tn.level_exit()
r = tn.go_to_onu_interface(stack_num, rack_num, fiber_num, new_onu_port_num)
print(r)
tn.apply_conf_to_onu(onu_mac, 145)
sleep(1)
return
if __name__ == '__main__':
ip = '10.40.1.10'
try:
register_onu_ZTE_F660(
olt_ip=ip, onu_sn=b'ZTEGC0458DCE', login_passwd=(b'admin', b'2ekc3'),
onu_mac=b'cc:7b:35:8b:7:0'
)
except ZteOltConsoleError as e:
print(e)
except ConnectionRefusedError:
print('ERROR: connection refused', ip)

2
djing/settings.py

@ -25,7 +25,7 @@ ALLOWED_HOSTS = local_settings.ALLOWED_HOSTS
# required for django-guardian # required for django-guardian
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'djing.auth_backends.CustomAuthBackend',
'djing.lib.auth_backends.CustomAuthBackend',
# 'django.contrib.auth.backends.ModelBackend', # default # 'django.contrib.auth.backends.ModelBackend', # default
'guardian.backends.ObjectPermissionBackend' 'guardian.backends.ObjectPermissionBackend'
) )

2
docs/dev.md

@ -343,7 +343,7 @@ def check_news(request):
return HttpResponse(dumps(r)) return HttpResponse(dumps(r))
``` ```
Убедитесь что вашему представлению не будет доступа от абонентов, об этом позаботится декоратор *only_admins* из *mydefs*. *mydefs* лежит в корне проекта.
Убедитесь что вашему представлению не будет доступа от абонентов, об этом позаботится декоратор *only_admins* из *djing.lib*.
После получения сообщения надо вернуть словарь с параметрами: После получения сообщения надо вернуть словарь с параметрами:

25
group_app/templates/group_app/group_confirm_delete.html

@ -1,13 +1,14 @@
{% extends 'base_delete_modal.html' %}
{% load i18n %} {% load i18n %}
<form role="form" action="{% url 'group_app:del' object.pk %}" method="post">{% csrf_token %}
<div class="modal-header warning">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-list-alt"></span>{% trans 'Remove group' %}</h4>
</div>
<div class="modal-body">
<h4>{% blocktrans %}Are you sure you want to delete group {{ object }}?{% endblocktrans %}</h4>
<button type="submit" class="btn btn-danger" value="DELETE">
<span class="glyphicon glyphicon-remove"></span> {% trans 'Delete' %}
</button>
</div>
</form>
{% block modal_form_url %}
{% url 'group_app:del' object.pk %}
{% endblock %}
{% block modal_form_title %}
{% trans 'Remove group' %}
{% endblock %}
{% block modal_form_text %}
<h4>{% blocktrans %}Are you sure you want to delete group {{ object }}?{% endblocktrans %}</h4>
{% endblock %}

2
mapapp/views.py

@ -14,7 +14,7 @@ from jsonview.decorators import json_view
from group_app.models import Group from group_app.models import Group
from .models import Dot from .models import Dot
from .forms import DotForm from .forms import DotForm
from mydefs import safe_int
from djing.lib import safe_int
from devapp.models import Device from devapp.models import Device
from guardian.decorators import permission_required from guardian.decorators import permission_required

7
messaging/sms/__init__.py

@ -1,7 +0,0 @@
# See LICENSE
from messaging.sms.submit import SmsSubmit
from messaging.sms.deliver import SmsDeliver
from messaging.sms.gsm0338 import is_gsm_text
__all__ = ["SmsSubmit", "SmsDeliver", "is_gsm_text"]

359
migrate_to_0.2.py

@ -1,359 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import shutil
from json import dump
import django
'''
Some permissions is not migrates, all admins is superuser
after migrate.
'''
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
from django.db.models import fields as django_fields
def get_fixture_from_unchanget_model(model_name: str, model_class):
"""
Создаёт фикстуру если модели между версиями не изменились
:param model_name: str 'app_label.model_name'
:param model_class: Model модель для которой надо сделать фикстуру
:return: список словарей
"""
print(model_name)
def get_fields(obj):
fields = dict()
for field in obj._meta.get_fields():
if isinstance(field,
(django_fields.reverse_related.ManyToOneRel, django_fields.reverse_related.ManyToManyRel)):
continue
field_val = getattr(obj, field.name)
if field_val is None:
continue
if isinstance(field, django_fields.related.ManyToManyField):
fields[field.name] = [f.pk for f in field_val.all()]
elif isinstance(field,
(django_fields.related.ForeignKey, django.contrib.contenttypes.fields.GenericForeignKey)):
fields[field.name] = field_val.pk
elif isinstance(field, django_fields.FloatField):
fields[field.name] = float(field_val)
elif isinstance(field, django_fields.DateTimeField):
fields[field.name] = str(field_val)
elif isinstance(field, django_fields.AutoField):
continue
else:
fields[field.name] = field_val
return fields
res = [{
'model': model_name,
'pk': obj.pk,
'fields': get_fields(obj)
} for obj in model_class.objects.all()]
return res
def dump_groups():
from abonapp import models
print('group_app.group')
res = [{
'model': 'group_app.group',
'pk': abon_group.pk,
'fields': {
'title': abon_group.title
}
} for abon_group in models.AbonGroup.objects.all()]
return res
def dump_abonapp():
from abonapp import models
res = get_fixture_from_unchanget_model('abonapp.abontariff', models.AbonTariff)
res += get_fixture_from_unchanget_model('abonapp.abonstreet', models.AbonStreet)
res += get_fixture_from_unchanget_model('abonapp.extrafieldsmodel', models.ExtraFieldsModel)
# res += get_fixture_from_unchanget_model('abonapp.abonlog', models.AbonLog)
print('abonapp.abon')
res += [{
'model': 'abonapp.abon',
'pk': abon.pk,
'fields': {
'current_tariff': abon.current_tariff.pk if abon.current_tariff else None,
'group': abon.group.pk if abon.group else None,
'ballance': abon.ballance,
'ip_address': abon.ip_address,
'description': abon.description,
'street': abon.street.pk if abon.street else None,
'house': abon.house,
'extra_fields': [a.pk for a in abon.extra_fields.all()],
'device': abon.device.pk if abon.device else None,
'dev_port': abon.dev_port if abon.dev_port else None,
'is_dynamic_ip': abon.is_dynamic_ip,
'markers': abon.markers
}
} for abon in models.Abon.objects.filter(is_admin=False)]
res += get_fixture_from_unchanget_model('abonapp.passportinfo', models.PassportInfo)
res += get_fixture_from_unchanget_model('abonapp.invoiceforpayment', models.InvoiceForPayment)
res += get_fixture_from_unchanget_model('abonapp.alltimepaylog', models.AllTimePayLog)
res += get_fixture_from_unchanget_model('abonapp.abonrawpassword', models.AbonRawPassword)
res += get_fixture_from_unchanget_model('abonapp.additionaltelephone', models.AdditionalTelephone)
res += get_fixture_from_unchanget_model('abonapp.periodicpayforid', models.PeriodicPayForId)
return res
def dump_tariffs():
from tariff_app import models
from abonapp.models import AbonGroup
print('tariff_app.tariff')
res = [{
'model': 'tariff_app.tariff',
'pk': trf.pk,
'fields': {
'title': trf.title,
'descr': trf.descr,
'speedIn': trf.speedIn,
'speedOut': trf.speedOut,
'amount': trf.amount,
'calc_type': trf.calc_type,
'is_admin': trf.is_admin,
'groups': [ag.pk for ag in AbonGroup.objects.filter(tariffs__in=[trf])]
}
} for trf in models.Tariff.objects.all()]
res += get_fixture_from_unchanget_model('tariff_app.periodicpay', models.PeriodicPay)
return res
def dump_devs():
from devapp import models
print('devapp.device')
res = [{
'model': 'devapp.device',
'pk': dv.pk,
'fields': {
'ip_address': dv.ip_address,
'mac_addr': str(dv.mac_addr) if dv.mac_addr else None,
'comment': dv.comment,
'devtype': dv.devtype,
'man_passw': dv.man_passw,
'group': dv.user_group.pk if dv.user_group else None,
'parent_dev': dv.parent_dev.pk if dv.parent_dev else None,
'snmp_item_num': dv.snmp_item_num,
'status': dv.status,
'is_noticeable': dv.is_noticeable
}
} for dv in models.Device.objects.all()]
res += get_fixture_from_unchanget_model('devapp.port', models.Port)
return res
def dump_accounts():
from accounts_app import models
from abonapp.models import AbonGroup
def get_responsibility_groups(account):
responsibility_groups = AbonGroup.objects.filter(profiles__in=[account])
ids = [ag.pk for ag in responsibility_groups]
return ids
print('accounts_app.baseaccount')
res = [{
'model': 'accounts_app.baseaccount',
'pk': up.pk,
'fields': {
'username': up.username,
'fio': up.fio,
'birth_day': up.birth_day,
'is_active': up.is_active,
'is_admin': up.is_admin,
'telephone': up.telephone,
'password': up.password,
'last_login': up.last_login,
'is_superuser': up.is_admin
}
} for up in models.UserProfile.objects.all()]
print('accounts_app.userprofile')
res += [{
'model': 'accounts_app.userprofile',
'pk': up.pk,
'fields': {
'avatar': up.avatar.pk if up.avatar else None,
'email': up.email,
'responsibility_groups': get_responsibility_groups(up)
}
} for up in models.UserProfile.objects.filter(is_admin=True)]
return res
def dump_photos():
from photo_app.models import Photo
print('photo_app.photo')
res = [{
'model': 'photo_app.photo',
'pk': p.pk,
'fields': {
'image': "%s" % p.image,
'wdth': p.wdth,
'heigt': p.heigt
}
} for p in Photo.objects.all()]
return res
def dump_chatbot():
from chatbot import models
res = get_fixture_from_unchanget_model('chatbot.telegrambot', models.TelegramBot)
res += get_fixture_from_unchanget_model('chatbot.messagehistory', models.MessageHistory)
res += get_fixture_from_unchanget_model('chatbot.messagequeue', models.MessageQueue)
return res
def dump_map():
from mapapp import models
res = get_fixture_from_unchanget_model('mapapp.dot', models.Dot)
return res
def dump_task_app():
from taskapp import models
res = get_fixture_from_unchanget_model('taskapp.changelog', models.ChangeLog)
res += get_fixture_from_unchanget_model('taskapp.task', models.Task)
res += get_fixture_from_unchanget_model('taskapp.ExtraComment', models.ExtraComment)
return res
def dump_auth():
from django.contrib.auth import models
from django.contrib.contenttypes.models import ContentType
res = get_fixture_from_unchanget_model('contenttypes.contenttype', ContentType)
res += get_fixture_from_unchanget_model('auth.group', models.Group)
res += get_fixture_from_unchanget_model('auth.permission', models.Permission)
return res
def dump_guardian():
from guardian import models
print('guardian.groupobjectpermission')
res = [{
'model': 'guardian.groupobjectpermission',
'pk': gp.pk,
'fields': {
'group': gp.group.pk,
'permission': gp.permission.pk,
'content_type': gp.content_type.pk,
'object_pk': str(gp.object_pk),
'content_object': gp.content_object.pk
}
} for gp in models.GroupObjectPermission.objects.all()]
print('guardian.userobjectpermission')
res += [{
'model': 'guardian.userobjectpermission',
'pk': up.pk,
'fields': {
'permission': up.permission.pk,
'content_type': up.content_type.pk,
'object_pk': str(up.object_pk),
'user': up.user.pk
}
} for up in models.UserObjectPermission.objects.all()]
return res
def make_migration():
from datetime import datetime, date
def my_date_converter(o):
if isinstance(o, datetime) or isinstance(o, date):
return "%s" % o
def appdump(path, func):
fname = os.path.join(*path)
path_dir = os.path.join(*path[:-1])
if not os.path.isdir(path_dir):
os.mkdir(path_dir)
with open(fname, 'w') as f:
dump(func(), f, default=my_date_converter, ensure_ascii=False)
if not os.path.isdir('fixtures'):
os.mkdir('fixtures')
appdump(['fixtures', 'group_app', 'groups_fixture.json'], dump_groups)
appdump(['fixtures', 'tariff_app', 'tariffs_fixture.json'], dump_tariffs)
appdump(['fixtures', 'photo_app', 'photos_fixture.json'], dump_photos)
appdump(['fixtures', 'devapp', 'devs_fixture.json'], dump_devs)
appdump(['fixtures', 'accounts_app', 'accounts_fixture.json'], dump_accounts)
appdump(['fixtures', 'abonapp', 'abon_fixture.json'], dump_abonapp)
appdump(['fixtures', 'chatbot', 'chatbot_fixture.json'], dump_chatbot)
appdump(['fixtures', 'mapapp', 'map_fixture.json'], dump_map)
appdump(['fixtures', 'taskapp', 'task_fixture.json'], dump_task_app)
# appdump(['fixtures', 'accounts_app', 'auth_fixture.json'], dump_auth)
# appdump(['fixtures', 'accounts_app', 'guardian_fixture.json'], dump_guardian)
def move_to_fixtures_dirs():
fixdir = 'fixtures'
for dr in os.listdir(fixdir):
fixture_dir = os.path.join(fixdir, dr)
for fixture_file in os.listdir(fixture_dir):
from_file = os.path.join(fixture_dir, fixture_file)
dst_dir = os.path.join(dr, fixdir)
to_file = os.path.join(dst_dir, fixture_file)
if not os.path.isdir(dst_dir):
os.mkdir(dst_dir)
shutil.copyfile(from_file, to_file)
print('cp %s -> %s' % (from_file, to_file))
def apply_fixtures():
from django.core.management import execute_from_command_line
from accounts_app.models import UserProfile
# from django.contrib.auth import models
UserProfile.objects.filter(username='AnonymousUser').delete()
# print('clearing auth.group')
# models.Group.objects.all().delete()
# print('clearing auth.permission')
# models.Permission.objects.all().delete()
fixtures_names = [
'groups_fixture.json', 'tariffs_fixture.json', 'photos_fixture.json',
'devs_fixture.json', 'accounts_fixture.json', 'abon_fixture.json',
'chatbot_fixture.json', 'map_fixture.json', 'task_fixture.json'
]
# 'auth_fixture.json', 'guardian_fixture.json'
print('./manage.py loaddata ' + ', '.join(fixtures_names))
execute_from_command_line([sys.argv[0], 'loaddata'] + fixtures_names)
if __name__ == '__main__':
if len(sys.argv) < 2:
print('Usage: ./migrate_to_0.2.py [makedump OR applydump]')
exit(1)
choice = sys.argv[1]
if choice == 'applydump':
django.setup()
move_to_fixtures_dirs()
apply_fixtures()
shutil.rmtree('fixtures')
elif choice == 'makedump':
django.setup()
make_migration()
else:
print('Unexpected choice')

2
periodic.py

@ -9,7 +9,7 @@ from django.db import transaction
from django.db.models import signals from django.db.models import signals
from abonapp.models import Abon, AbonTariff, abontariff_pre_delete, PeriodicPayForId, AbonLog from abonapp.models import Abon, AbonTariff, abontariff_pre_delete, PeriodicPayForId, AbonLog
from agent import Transmitter, NasNetworkError, NasFailedResult from agent import Transmitter, NasNetworkError, NasFailedResult
from mydefs import LogicError
from djing.lib import LogicError
def main(): def main():

18
static/css/custom.css

@ -110,7 +110,7 @@ td.btn-group {
border-left: 1px solid #ddd; border-left: 1px solid #ddd;
border-right: 1px solid #ddd; border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
border-radius: 0px 0px 5px 5px;
border-radius: 0 0 5px 5px;
padding: 10px; padding: 10px;
} }
@ -209,8 +209,7 @@ a.port-img{
left: 0; left: 0;
} }
.row-offcanvas-left
.sidebar-offcanvas {
.row-offcanvas-left .sidebar-offcanvas {
left: -50%; /* 6 columns */ left: -50%; /* 6 columns */
} }
@ -299,3 +298,16 @@ pre {
.m-icon_dollar{background-position: 0 -52px;} .m-icon_dollar{background-position: 0 -52px;}
.m-icon_service{background-position: -26px -52px;} .m-icon_service{background-position: -26px -52px;}
.m-icon_mrk{background-position: -52px -52px;} .m-icon_mrk{background-position: -52px -52px;}
@media (max-width: 991px) and (min-width: 768px){
.container {
width: 874px;
}
}
/* Icon near to device info */
.font-extra-large{
font-size: 75px;
}

5
statistics/migrations/0001_initial.py

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import mydefs
from djing.lib import MyGenericIPAddressField
import statistics.fields import statistics.fields
@ -18,8 +18,7 @@ class Migration(migrations.Migration):
name='StatCache', name='StatCache',
fields=[ fields=[
('last_time', statistics.fields.UnixDateTimeField()), ('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)), ('octets', models.PositiveIntegerField(default=0)),
('packets', models.PositiveIntegerField(default=0)), ('packets', models.PositiveIntegerField(default=0)),
], ],

2
statistics/models.py

@ -3,7 +3,7 @@ from datetime import datetime, timedelta, date, time
from django.db import models, connection, ProgrammingError from django.db import models, connection, ProgrammingError
from django.utils.timezone import now from django.utils.timezone import now
from mydefs import MyGenericIPAddressField
from djing.lib import MyGenericIPAddressField
from .fields import UnixDateTimeField from .fields import UnixDateTimeField

4
tariff_app/base_intr.py

@ -17,7 +17,7 @@ class TariffBase(metaclass=ABCMeta):
@staticmethod @staticmethod
def description() -> AnyStr: def description() -> AnyStr:
""" """
Usage in mydefs.MyChoicesAdapter for choices fields.
Usage in djing.lib.MyChoicesAdapter for choices fields.
:return: human readable description :return: human readable description
""" """
raise NotImplementedError raise NotImplementedError
@ -54,5 +54,5 @@ class PeriodicPayCalcBase(metaclass=ABCMeta):
@staticmethod @staticmethod
def description() -> AnyStr: def description() -> AnyStr:
"""Return text description. """Return text description.
Uses in mydefs.MyChoicesAdapter for CHOICES fields"""
Uses in djing.lib.MyChoicesAdapter for CHOICES fields"""
raise NotImplementedError raise NotImplementedError

3
tariff_app/locale/ru/LC_MESSAGES/django.po

@ -209,3 +209,6 @@ msgstr "Новый периодический платёж создан"
#: tariff_app/views.py:102 #: tariff_app/views.py:102
msgid "Periodic pay has been changed" msgid "Periodic pay has been changed"
msgstr "Периодический платёж изменён" msgstr "Периодический платёж изменён"
msgid "Are you sure you want to delete tariff?"
msgstr "Вы уверены что хотите удалить тариф?"

2
tariff_app/models.py

@ -5,7 +5,7 @@ from django.dispatch import receiver
from .base_intr import TariffBase, PeriodicPayCalcBase from .base_intr import TariffBase, PeriodicPayCalcBase
from .custom_tariffs import TARIFF_CHOICES, PERIODIC_PAY_CHOICES from .custom_tariffs import TARIFF_CHOICES, PERIODIC_PAY_CHOICES
from group_app.models import Group from group_app.models import Group
from mydefs import MyChoicesAdapter
from djing.lib import MyChoicesAdapter
from jsonfield import JSONField from jsonfield import JSONField

30
tariff_app/templates/tariff_app/modal_del_warning.html

@ -1,30 +0,0 @@
{% load i18n %}
<form role="form" action="{% url 'tarifs:del' tid %}" method="post"> {% csrf_token %}
<div class="modal-header warning">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-exclamation-sign"></span>{% trans 'Delete service' %}</h4>
</div>
{% include 'message_block.html' %}
<div class="modal-body">
<div class="form-group-sm">
<label for="id_dev">{% trans 'Attention' %}</label>
<p>{% blocktrans %}after delete the tariff, subscribers who use that tariff will be disconnected from it.{% endblocktrans %}</p>
<input type="hidden" name="confirm" value="yes">
</div>
<div class="btn-group">
<button type="submit" class="btn btn-sm btn-danger">
<span class="glyphicon glyphicon-remove"></span> {% trans 'Delete' %}
</button>
<button type="reset" class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-retweet"></span> {% trans 'Reset' %}
</button>
</div>
</div>
</form>

15
tariff_app/templates/tariff_app/tariff_confirm_delete.html

@ -0,0 +1,15 @@
{% extends 'base_delete_modal.html' %}
{% load i18n %}
{% block modal_form_url %}
{% url 'tarifs:del' tid %}
{% endblock %}
{% block modal_form_title %}
{% trans 'Delete service' %}
{% endblock %}
{% block modal_form_text %}
<label>{% trans 'Are you sure you want to delete tariff?' %}</label>
<p>{% blocktrans %}after delete the tariff, subscribers who use that tariff will be disconnected from it.{% endblocktrans %}</p>
{% endblock %}

2
tariff_app/urls.py

@ -8,7 +8,7 @@ urlpatterns = [
url(r'^$', views.TariffsListView.as_view(), name='home'), url(r'^$', views.TariffsListView.as_view(), name='home'),
url(r'^(?P<tarif_id>\d+)$', views.edit_tarif, name='edit'), url(r'^(?P<tarif_id>\d+)$', views.edit_tarif, name='edit'),
url(r'^add$', views.edit_tarif, name='add'), url(r'^add$', views.edit_tarif, name='add'),
url(r'^del(?P<tid>\d+)$', views.del_tarif, name='del'),
url(r'^del(?P<tid>\d+)$', views.TariffDeleteView.as_view(), name='del'),
url(r'^periodic_pays$', views.PeriodicPaysListView.as_view(), name='periodic_pays'), url(r'^periodic_pays$', views.PeriodicPaysListView.as_view(), name='periodic_pays'),
url(r'^periodic_pays/add$', views.periodic_pay, name='periodic_pay_add'), url(r'^periodic_pays/add$', views.periodic_pay, name='periodic_pay_add'),

38
tariff_app/views.py

@ -1,18 +1,18 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.gis.shortcuts import render_to_text from django.contrib.gis.shortcuts import render_to_text
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.views.generic import ListView
from django.views.generic import ListView, DeleteView
from django.conf import settings from django.conf import settings
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
from djing.global_base_views import OrderingMixin from djing.global_base_views import OrderingMixin
from .models import Tariff, PeriodicPay from .models import Tariff, PeriodicPay
import mydefs
from djing import lib
from . import forms from . import forms
@ -21,7 +21,7 @@ class BaseServiceListView(ListView):
paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
@method_decorator((login_required, mydefs.only_admins), name='dispatch')
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
class TariffsListView(BaseServiceListView, OrderingMixin): class TariffsListView(BaseServiceListView, OrderingMixin):
""" """
Show Services(Tariffs) list Show Services(Tariffs) list
@ -33,7 +33,7 @@ class TariffsListView(BaseServiceListView, OrderingMixin):
@login_required @login_required
def edit_tarif(request, tarif_id=0): 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 tarif_id == 0:
if not request.user.has_perm('tariff_app.add_tariff'): if not request.user.has_perm('tariff_app.add_tariff'):
@ -61,21 +61,25 @@ def edit_tarif(request, tarif_id=0):
}) })
@login_required
@permission_required('tariff_app.delete_tariff')
def del_tarif(request, tid):
if request.method == 'POST':
if request.POST.get('confirm') == 'yes':
get_object_or_404(Tariff, id=tid).delete()
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 render_to_text('tariff_app/modal_del_warning.html', {'tid': tid}, request=request)
@method_decorator(login_required, name='dispatch')
@method_decorator(permission_required('tariff_app.delete_tariff'), name='dispatch')
class TariffDeleteView(DeleteView):
model = Tariff
pk_url_kwarg = 'tid'
success_url = reverse_lazy('tarifs:home')
def delete(self, request, *args, **kwargs):
res = super().delete(request, *args, **kwargs)
messages.success(request, _('Service has been deleted'))
return res
def get_context_data(self, **kwargs):
kwargs['tid'] = self.kwargs.get('tid')
return super().get_context_data(**kwargs)
@method_decorator(login_required, name='dispatch') @method_decorator(login_required, name='dispatch')
@method_decorator(permission_required('tariff_app.can_view_periodic_pay'), name='dispatch')
@method_decorator(permission_required('tariff_app.delete_tariff'), name='dispatch')
class PeriodicPaysListView(BaseServiceListView): class PeriodicPaysListView(BaseServiceListView):
context_object_name = 'pays' context_object_name = 'pays'
model = PeriodicPay model = PeriodicPay

2
taskapp/handle.py

@ -2,7 +2,7 @@
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from chatbot.telebot import send_notify from chatbot.telebot import send_notify
from chatbot.models import ChatException from chatbot.models import ChatException
from mydefs import MultipleException
from djing.lib import MultipleException
class TaskException(Exception): class TaskException(Exception):

2
taskapp/migrations/0001_initial.py

@ -51,7 +51,7 @@ class Migration(migrations.Migration):
('priority', ('priority',
models.CharField(choices=[('A', 'Higher'), ('C', 'Average'), ('E', 'Low')], default='E', max_length=1, models.CharField(choices=[('A', 'Higher'), ('C', 'Average'), ('E', 'Low')], default='E', max_length=1,
verbose_name='A priority')), 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')), verbose_name='Reality')),
('time_of_create', models.DateTimeField(auto_now_add=True, verbose_name='Date of create')), ('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', ('state', models.CharField(choices=[('S', 'New'), ('C', 'Confused'), ('F', 'Completed')], default='S',

8
taskapp/views.py

@ -1,5 +1,3 @@
# coding=utf-8
from json import dumps
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import HttpResponse from django.http import HttpResponse
@ -15,12 +13,15 @@ from datetime import datetime
from django.views.generic.edit import FormMixin, DeleteView, UpdateView from django.views.generic.edit import FormMixin, DeleteView, UpdateView
from guardian.decorators import permission_required_or_403 as permission_required from guardian.decorators import permission_required_or_403 as permission_required
from jsonview.decorators import json_view
from chatbot.models import MessageQueue from chatbot.models import MessageQueue
from abonapp.models import Abon from abonapp.models import Abon
from djing import httpresponse_to_referrer from djing import httpresponse_to_referrer
from djing.lib import safe_int, MultipleException, RuTimedelta
from djing.lib.decorators import only_admins
from .handle import TaskException from .handle import TaskException
from .models import Task, ExtraComment from .models import Task, ExtraComment
from mydefs import only_admins, safe_int, MultipleException, RuTimedelta
from .forms import TaskFrm, ExtraCommentForm from .forms import TaskFrm, ExtraCommentForm
@ -249,6 +250,7 @@ def remind(request, task_id):
return redirect('taskapp:home') return redirect('taskapp:home')
@json_view
def check_news(request): def check_news(request):
if request.user.is_authenticated and request.user.is_admin: if request.user.is_authenticated and request.user.is_admin:
msg = MessageQueue.objects.pop(user=request.user, tag='taskap') msg = MessageQueue.objects.pop(user=request.user, tag='taskap')

19
templates/base_delete_modal.html

@ -0,0 +1,19 @@
{% load i18n %}
<form role="form" action="{% block modal_form_url %}#modal_url{% endblock %}" method="post">{% csrf_token %}
<div class="modal-header warning">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">
<span class="glyphicon glyphicon-earphone"></span>
{% block modal_form_title %}Form title{% endblock %}
</h4>
</div>
<div class="modal-body">
{% block modal_form_text %}
<h4>{% trans 'Are you sure about them?' %}</h4>
{% endblock %}
<button type="submit" class="btn btn-danger" value="DELETE">
<span class="glyphicon glyphicon-remove"></span>
{% block modal_btn_delete_text %}{% trans 'Delete' %}{% endblock %}
</button>
</div>
</form>
Loading…
Cancel
Save