Browse Source

little bit refactoring

devel
bashmak 8 years ago
parent
commit
a768482912
  1. 2
      abonapp/models.py
  2. 2
      abonapp/templates/abonapp/peoples.html
  3. 17
      abonapp/views.py
  4. 2
      agent/commands/dhcp.py
  5. 29
      devapp/base_intr.py
  6. 81
      devapp/dev_types.py
  7. 4
      devapp/models.py
  8. 2
      devapp/views.py
  9. 18
      djing/lib/__init__.py
  10. 10
      djing/lib/decorators.py
  11. 19
      docs/dev.md
  12. 2
      ip_pool/templates/ip_pool/net_add.html
  13. 4
      mapapp/urls.py
  14. 34
      mapapp/views.py
  15. 20
      tariff_app/base_intr.py
  16. 30
      tariff_app/custom_tariffs.py

2
abonapp/models.py

@ -162,7 +162,7 @@ class Abon(BaseAccount):
Return icon list of set flags from self.markers
:return: ['m-icon-donkey', 'm-icon-tv', ...]
"""
return ["m-%s" % name for name, state in self.markers if state]
return tuple("m-%s" % name for name, state in self.markers if state)
def is_markers_empty(self):
return int(self.markers) == 0

2
abonapp/templates/abonapp/peoples.html

@ -10,6 +10,8 @@
</ol>
{% endblock %}
{% block page-header %}{{ group.title }}{% endblock %}
{% block main %}
<div class="row">
<div class="col-lg-10 col-md-8">

17
abonapp/views.py

@ -30,16 +30,11 @@ from guardian.shortcuts import get_objects_for_user, assign_perm
from guardian.decorators import permission_required_or_403 as permission_required
from djing import ping
from djing import lib
from djing.global_base_views import OrderingMixin, BaseListWithFiltering, SecureApiView
class BaseAbonListView(OrderingMixin, BaseListWithFiltering):
paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
http_method_names = ('get',)
from djing.global_base_views import BaseOrderedFilteringList, SecureApiView
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
class PeoplesListView(BaseAbonListView):
class PeoplesListView(BaseOrderedFilteringList):
context_object_name = 'peoples'
template_name = 'abonapp/peoples.html'
@ -84,7 +79,7 @@ class PeoplesListView(BaseAbonListView):
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
class GroupListView(BaseAbonListView):
class GroupListView(BaseOrderedFilteringList):
context_object_name = 'groups'
template_name = 'abonapp/group_list.html'
queryset = Group.objects.annotate(usercount=Count('abon'))
@ -212,7 +207,7 @@ def abonamount(request, gid, uname):
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class DebtsListView(BaseAbonListView):
class DebtsListView(BaseOrderedFilteringList):
context_object_name = 'invoices'
template_name = 'abonapp/invoiceForPayment.html'
@ -230,7 +225,7 @@ class DebtsListView(BaseAbonListView):
@method_decorator((login_required, lib.decorators.only_admins), name='dispatch')
@method_decorator(permission_required('group_app.can_view_group', (Group, 'pk', 'gid')), name='dispatch')
class PayHistoryListView(BaseAbonListView):
class PayHistoryListView(BaseOrderedFilteringList):
context_object_name = 'pay_history'
template_name = 'abonapp/payHistory.html'
@ -756,7 +751,7 @@ def abon_ping(request):
@method_decorator((login_required, lib.decorators.only_admins,), name='dispatch')
class DialsListView(BaseAbonListView):
class DialsListView(BaseOrderedFilteringList):
context_object_name = 'logs'
template_name = 'abonapp/dial_log.html'

2
agent/commands/dhcp.py

@ -9,7 +9,7 @@ def dhcp_commit(client_ip: str, client_mac: str, switch_mac: str, switch_port: i
dev = Device.objects.get(mac_addr=switch_mac)
mngr_class = dev.get_manager_klass()
if mngr_class.is_use_device_port():
if mngr_class.get_is_use_device_port():
abon = Abon.objects.get(dev_port__device=dev,
dev_port__num=switch_port,
device=dev)

29
devapp/base_intr.py

@ -5,15 +5,13 @@ from easysnmp import Session
from django.utils.translation import gettext
from djing.lib.decorators import abstract_static_method
ListOrError = Union[
Iterable,
Union[Exception, Iterable]
]
class DeviceImplementationError(Exception):
class DeviceImplementationError(NotImplementedError):
pass
@ -25,10 +23,15 @@ class DevBase(object, metaclass=ABCMeta):
def __init__(self, dev_instance=None):
self.db_instance = dev_instance
@abstract_static_method
def description() -> AnyStr:
@property
@abstractmethod
def description(self) -> AnyStr:
pass
@classmethod
def get_description(cls):
return cls.description
@abstractmethod
def reboot(self):
pass
@ -49,23 +52,29 @@ class DevBase(object, metaclass=ABCMeta):
def get_template_name(self) -> AnyStr:
"""Return path to html template for device"""
@property
@abstractmethod
def has_attachable_to_subscriber(self) -> bool:
"""Can connect device to subscriber"""
@abstract_static_method
def is_use_device_port() -> bool:
@property
@abstractmethod
def is_use_device_port(self) -> bool:
"""True if used device port while opt82 authorization"""
# fixme: only that is abstract static
@abstract_static_method
def validate_extra_snmp_info(v: str) -> None:
@classmethod
def get_is_use_device_port(cls) -> bool:
return cls.is_use_device_port
@classmethod
def validate_extra_snmp_info(cls, 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
"""
raise NotImplementedError
@abstractmethod
def register_device(self, extra_data: Dict):

81
devapp/dev_types.py

@ -60,14 +60,14 @@ class DLinkPort(BasePort):
class DLinkDevice(DevBase, SNMPBaseWorker):
has_attachable_to_subscriber = True
description = _('DLink switch')
is_use_device_port = True
def __init__(self, dev_instance):
DevBase.__init__(self, dev_instance)
SNMPBaseWorker.__init__(self, dev_instance.ip_address, dev_instance.man_passw, 2)
@staticmethod
def description():
return _('DLink switch')
def reboot(self):
return self.get_item('.1.3.6.1.4.1.2021.8.1.101.1')
@ -103,15 +103,8 @@ class DLinkDevice(DevBase, SNMPBaseWorker):
def get_template_name(self):
return 'ports.html'
def has_attachable_to_subscriber(self) -> bool:
return True
@staticmethod
def is_use_device_port():
return True
@staticmethod
def validate_extra_snmp_info(v: str) -> None:
@classmethod
def validate_extra_snmp_info(cls, v: str) -> None:
# Dlink has no require snmp info
pass
@ -142,14 +135,14 @@ class ONUdev(BasePort):
class OLTDevice(DevBase, SNMPBaseWorker):
has_attachable_to_subscriber = False
description = _('PON OLT')
is_use_device_port = False
def __init__(self, dev_instance):
DevBase.__init__(self, dev_instance)
SNMPBaseWorker.__init__(self, dev_instance.ip_address, dev_instance.man_passw, 2)
@staticmethod
def description():
return gettext('PON OLT')
def reboot(self):
pass
@ -187,15 +180,8 @@ class OLTDevice(DevBase, SNMPBaseWorker):
def get_template_name(self):
return 'olt.html'
def has_attachable_to_subscriber(self) -> bool:
return False
@staticmethod
def is_use_device_port():
return False
@staticmethod
def validate_extra_snmp_info(v: str) -> None:
@classmethod
def validate_extra_snmp_info(cls, v: str) -> None:
# Olt has no require snmp info
pass
@ -208,6 +194,10 @@ class OLTDevice(DevBase, SNMPBaseWorker):
class OnuDevice(DevBase, SNMPBaseWorker):
has_attachable_to_subscriber = True
description = _('PON ONU')
is_use_device_port = False
def __init__(self, dev_instance):
DevBase.__init__(self, dev_instance)
dev_ip_addr = None
@ -223,15 +213,11 @@ class OnuDevice(DevBase, SNMPBaseWorker):
))
SNMPBaseWorker.__init__(self, dev_ip_addr, dev_instance.man_passw, 2)
@staticmethod
def description() -> AnyStr:
return gettext('PON ONU')
def reboot(self):
pass
def get_ports(self) -> ListOrError:
return []
return ()
def get_device_name(self):
pass
@ -242,13 +228,6 @@ class OnuDevice(DevBase, SNMPBaseWorker):
def get_template_name(self):
return "onu.html"
def has_attachable_to_subscriber(self) -> bool:
return True
@staticmethod
def is_use_device_port():
return False
def get_details(self):
if self.db_instance is None:
return
@ -274,7 +253,7 @@ class OnuDevice(DevBase, SNMPBaseWorker):
return {'err': "%s: %s" % (_('ONU not connected'), e)}
@staticmethod
def validate_extra_snmp_info(v: str) -> None:
def validate_extra_snmp_info(cls, v: str) -> None:
# DBCOM Onu have en integer snmp port
try:
int(v)
@ -330,9 +309,8 @@ class EltexPort(BasePort):
class EltexSwitch(DLinkDevice):
@staticmethod
def description():
return _('Eltex switch')
description = _('Eltex switch')
is_use_device_port = False
def get_ports(self) -> ListOrError:
res = []
@ -355,13 +333,6 @@ class EltexSwitch(DLinkDevice):
tm = RuTimedelta(timedelta(seconds=uptimestamp / 100)) or RuTimedelta(timedelta())
return tm
def has_attachable_to_subscriber(self) -> bool:
return True
@staticmethod
def is_use_device_port():
return False
def monitoring_template(self, *args, **kwargs) -> Optional[str]:
device = self.db_instance
return plain_ip_device_mon_template(device)
@ -378,9 +349,7 @@ def conv_signal(lvl: int) -> float:
class Olt_ZTE_C320(OLTDevice):
@staticmethod
def description():
return gettext('OLT ZTE C320')
description = _('OLT ZTE C320')
def get_fibers(self):
fibers = ({
@ -443,9 +412,7 @@ class Olt_ZTE_C320(OLTDevice):
class ZteOnuDevice(OnuDevice):
@staticmethod
def description():
return _('ZTE PON ONU')
description = _('ZTE PON ONU')
def get_details(self) -> Optional[Dict]:
if self.db_instance is None:
@ -472,8 +439,8 @@ class ZteOnuDevice(OnuDevice):
def get_template_name(self):
return 'onu_for_zte.html'
@staticmethod
def validate_extra_snmp_info(v: str) -> None:
@classmethod
def validate_extra_snmp_info(cls, v: str) -> None:
# for example 268501760.5
try:
fiber_num, onu_port = v.split('.')

4
devapp/models.py

@ -90,8 +90,8 @@ class Device(models.Model):
# Can attach device to subscriber in subscriber page
def has_attachable_to_subscriber(self) -> bool:
mngr = self.get_manager_object()
return mngr.has_attachable_to_subscriber()
mngr = self.get_manager_klass()
return mngr.has_attachable_to_subscriber
def __str__(self):
return "%s: (%s) %s %s" % (self.comment, self.get_devtype_display(), self.ip_address or '', self.mac_addr or '')

2
devapp/views.py

@ -650,7 +650,7 @@ class OnDeviceMonitoringEvent(global_base_views.SecureApiView):
recipients = UserProfile.objects.get_profiles_by_group(device_down.group.pk)
names = list()
for recipient in recipients:
for recipient in recipients.iterator():
send_notify(
msg_text=gettext(notify_text) % {
'device_name': "%s(%s) %s" % (

18
djing/lib/__init__.py

@ -38,24 +38,16 @@ def safe_int(i):
# классы передавать для того чтоб по значению кода из базы понять какой класс нужно взять для нужной функциональности.
# Например по коду в базе вам нужно определять как считать тариф абонента, что реализовано в возвращаемом классе.
class MyChoicesAdapter(Iterator):
chs = tuple()
current_index = 0
_max_index = 0
_chs = None
# На вход принимает кортеж кортежей, вложенный из 2х элементов: кода и класса, как: TARIFF_CHOICES
def __init__(self, choices):
self._max_index = len(choices)
self.chs = choices
self._chs = iter(choices)
def __next__(self):
if self.current_index >= self._max_index:
raise StopIteration
else:
e = self.chs
ci = self.current_index
res = e[ci][0], e[ci][1].description()
self.current_index += 1
return res
obj = next(self._chs)
choice_code, choice_class = obj
return choice_code, choice_class.get_description()
# Russian localized timedelta

10
djing/lib/decorators.py

@ -55,13 +55,3 @@ def hash_auth_view(fn):
else:
return HttpResponseForbidden('Access Denied')
return wrapped
class abstract_static_method(staticmethod):
__slots__ = ()
def __init__(self, func):
super(abstract_static_method, self).__init__(func)
func.__isabstractmethod__ = True
__isabstractmethod__ = True

19
docs/dev.md

@ -47,10 +47,9 @@ class EltexPort(BasePort):
Теперь реализация для свича:
```python
class EltexSwitch(DLinkDevice):
@staticmethod
def description():
return _('Eltex switch')
has_attachable_to_subscriber = False
description = _('Eltex switch')
is_use_device_port = False
def get_ports(self):
#nams = self.get_list('.1.3.6.1.4.1.171.10.134.2.1.1.100.2.1.3')
@ -77,14 +76,8 @@ class EltexSwitch(DLinkDevice):
tm = RuTimedelta(timedelta(seconds=uptimestamp/100)) or RuTimedelta(timedelta())
return tm
def has_attachable_to_subscriber(self) -> bool:
return False
@staticmethod
def is_use_device_port():
return False
```
Метод **@description** Просто отображает человекопонятное название вашего устройства в биллинге.
Свойство **@description** Просто отображает человекопонятное название вашего устройства в биллинге.
Заметьте что строка на английском и заключена в процедуру **_** (это ugettext_lazy, см. в импорте вверху файла),
это локализация для текущего языка. Про локализацию можно почитать в соответствующем разделе [django translation](https://docs.djangoproject.com/en/1.11/topics/i18n/translation/).
@ -98,11 +91,11 @@ class EltexSwitch(DLinkDevice):
Метод **@uptime**, понятно что возвращает, укажите нужный OID. Вернётся тип *RuTimedelta*, это переопределённый тип **timedelta**, я его реализовал
для локализации временного промежутка на русский.
Метод **@has_attachable_to_subscriber** возвращает правду если это устройство можно привязать к абоненту.
Свойство **@has_attachable_to_subscriber** возвращает правду если это устройство можно привязать к абоненту.
Например у Dlink стоит True потому что Dlink стоит во многих местах на доступе, и его порты принадлежат
абонентам при авторизации.
Статический метод **@is_use_device_port** используется в DHCP чтоб понять что мы используем для привязки к абоненту всё устройство или
Свойство **@is_use_device_port** используется в DHCP чтоб понять что мы используем для привязки к абоненту всё устройство или
только порт устройства. Например, если у устройства только 1 порт абонента (PON ONU), и мы привязываем этого абонента ко всему устройству
а не к порту, то нужно вернуть False, На обычных свичах где мы авторизуем абонента на порту возвращаем True.

2
ip_pool/templates/ip_pool/net_add.html

@ -11,6 +11,8 @@
</ol>
{% endblock %}
{% block page-header %}{% trans 'Add new subnet' %}{% endblock %}
{% block main %}
<form action="{% url 'ip_pool:net_add' %}" method="post">{% csrf_token %}
<div class="panel panel-default">

4
mapapp/urls.py

@ -7,8 +7,8 @@ app_name = 'mapapp'
urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^options$', views.OptionsListView.as_view(), name='options'),
url(r'^options/add$', views.dot, name='add_dot'),
url(r'^options/(?P<did>\d+)/edit$', views.dot, name='edit_dot'),
url(r'^options/add$', views.dot_edit, name='add_dot'),
url(r'^options/(?P<did>\d+)/edit$', views.dot_edit, name='edit_dot'),
url(r'^options/(?P<did>\d+)/remove$', views.remove, name='remove_dot'),
url(r'^options/(?P<did>\d+)/add_dev$', views.add_dev, name='add_dev'),
url(r'^preload_devices$', views.preload_devices, name='preload_devices'),

34
mapapp/views.py

@ -31,8 +31,8 @@ def home(request):
dots = Dot.objects.all()
groups = Group.objects.all()
return render(request, 'maps/ya_index.html', {
'dots': dots,
'groups': groups
'dots': dots.iterator(),
'groups': groups.iterator()
})
@ -49,7 +49,7 @@ class OptionsListView(BaseListView):
@login_required
def dot(request, did=0):
def dot_edit(request, did=0):
if not request.user.is_superuser:
return redirect('/')
try:
@ -101,9 +101,9 @@ def remove(request, did):
def get_dots(request):
if not request.user.is_superuser:
return HttpResponseForbidden('you have not super user')
dots = Dot.objects.prefetch_related('devices').annotate(devcount=Count('devices')).defer('attachment')
dots = Dot.objects.prefetch_related('devices').annotate(devcount=Count('devices')).defer('attachment').iterator()
def fill_dev(dev):
def fill_dev(dev: Device):
return {
'status': dev.status,
'comment': dev.comment
@ -169,15 +169,15 @@ def modal_add_dot(request):
def preload_devices(request):
if not request.user.is_superuser:
return HttpResponseForbidden('you have not super user')
grp = request.GET.get('grp')
dot = request.GET.get('dot')
all_devices = Device.objects.filter(group__id=grp)
dot_devices = Device.objects.filter(dot__id=dot)
dot_devices_ids = [dev.pk for dev in dot_devices]
grp_id = request.GET.get('grp')
dot_id = request.GET.get('dot')
all_devices = Device.objects.filter(group__id=grp_id)
dot_devices = Device.objects.filter(dot__id=dot_id)
dot_devices_ids = tuple(dev.pk for dev in dot_devices.iterator())
del dot_devices
ret = render_to_text('maps/preload_devices_tmpl.html', {
'all_devices': all_devices,
'all_devices': all_devices.iterator(),
'dot_devices_ids': dot_devices_ids
})
return HttpResponse(ret, content_type='text/html')
@ -195,7 +195,7 @@ def dot_tooltip(request):
except Dot.DoesNotExist:
pass
return render_to_text('maps/map_tooltip.html', {
'devs': devs,
'devs': devs.iterator(),
'dot': dot
})
@ -213,8 +213,8 @@ def add_dev(request, did):
selected_user_group = safe_int(request.POST.get('selected_user_group'))
existing_devs = Device.objects.filter(group__id=selected_user_group or param_user_group)
if existing_devs.count() > 0:
dot.devices.remove(*[dev.pk for dev in existing_devs])
if existing_devs.exists():
dot.devices.remove(*(dev.pk for dev in existing_devs.iterator()))
dot.devices.add(*selected_devs)
url = resolve_url('mapapp:add_dev', did=dot.pk)
@ -222,9 +222,9 @@ def add_dev(request, did):
else:
existing_devs = Device.objects.filter(group=param_user_group)
return render(request, 'maps/add_device.html', {
'groups': groups,
'groups': groups.iterator(),
'dot': dot,
'existing_devs': existing_devs,
'existing_devs': existing_devs.iterator(),
'grp': param_user_group,
'dot_devices_ids': [dev.pk for dev in Device.objects.filter(dot=dot)]
})

20
tariff_app/base_intr.py

@ -14,13 +14,17 @@ class TariffBase(metaclass=ABCMeta):
"""Calculate deadline date"""
raise NotImplementedError
@staticmethod
def description() -> AnyStr:
@property
@abstractmethod
def description(self) -> AnyStr:
"""
Usage in djing.lib.MyChoicesAdapter for choices fields.
:return: human readable description
"""
raise NotImplementedError
@classmethod
def get_description(cls):
return cls.description
@staticmethod
def manage_access(abon) -> bool:
@ -51,8 +55,12 @@ class PeriodicPayCalcBase(metaclass=ABCMeta):
"""
raise NotImplementedError
@staticmethod
def description() -> AnyStr:
@property
@abstractmethod
def description(self) -> AnyStr:
"""Return text description.
Uses in djing.lib.MyChoicesAdapter for CHOICES fields"""
raise NotImplementedError
@classmethod
def get_description(cls):
return cls.description

30
tariff_app/custom_tariffs.py

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from datetime import timedelta, datetime
from typing import AnyStr
from django.utils import timezone
from django.utils.translation import gettext as _
@ -11,6 +10,8 @@ from random import uniform
class TariffDefault(TariffBase):
description = _('Base calculate functionality')
def __init__(self, abon_tariff):
# assert isinstance(abon_tariff, AbonTariff)
self.abon_tariff = abon_tariff
@ -44,25 +45,20 @@ class TariffDefault(TariffBase):
hour=23, minute=59, second=59)
return last_month_date
@staticmethod
def description() -> AnyStr:
return _('Base calculate functionality')
class TariffDp(TariffDefault):
description = 'IS'
# в IS снимается вся стоимость тарифа вне зависимости от времени использования
# просто возвращаем всю стоимость тарифа
def calc_amount(self) -> float:
return float(self.abon_tariff.tariff.amount)
@staticmethod
def description() -> AnyStr:
return 'IS'
# Как в IS только не на время, а на 10 лет
class TariffCp(TariffDp):
description = _('Private service')
def calc_deadline(self) -> datetime:
# делаем время окончания услуги на 10 лет вперёд
nw = timezone.now()
@ -70,10 +66,6 @@ class TariffCp(TariffDp):
hour=23, minute=59, second=59)
return long_long_time
@staticmethod
def description() -> AnyStr:
return _('Private service')
# Первый - всегда по умолчанию
TARIFF_CHOICES = (
@ -84,6 +76,8 @@ TARIFF_CHOICES = (
class PeriodicPayCalcDefault(PeriodicPayCalcBase):
description = _('Default periodic pay')
def calc_amount(self, model_object) -> float:
return model_object.amount
@ -91,12 +85,10 @@ class PeriodicPayCalcDefault(PeriodicPayCalcBase):
# TODO: решить какой будет расёт периодических платежей
return datetime.now() + timedelta(days=30)
@staticmethod
def description() -> AnyStr:
return _('Default periodic pay')
class PeriodicPayCalcCustom(PeriodicPayCalcDefault):
description = _('Custom periodic pay')
def calc_amount(self, model_object) -> float:
"""
:param model_object: it is a instance of models.PeriodicPay model
@ -104,10 +96,6 @@ class PeriodicPayCalcCustom(PeriodicPayCalcDefault):
"""
return uniform(1, 10)
@staticmethod
def description() -> AnyStr:
return _('Custom periodic pay')
PERIODIC_PAY_CHOICES = (
('df', PeriodicPayCalcDefault),

Loading…
Cancel
Save