Browse Source

Merge branch 'devel' into global_group_update

devel
Dmitry 8 years ago
parent
commit
91556518f3
  1. 12
      abonapp/templates/abonapp/peoples.html
  2. 17
      abonapp/views.py
  3. 2
      accounts_app/views.py
  4. 5
      agent/netflow/netflow_handler.py
  5. 2
      chatbot/models.py
  6. 143
      clientsideapp/locale/ru/LC_MESSAGES/django.po
  7. 1
      clientsideapp/templates/clientsideapp/custom_pages/footer.htm
  8. 1
      clientsideapp/templates/clientsideapp/custom_pages/main_page.htm
  9. 2
      clientsideapp/templates/clientsideapp/custom_pages/service.htm
  10. 1
      clientsideapp/templates/clientsideapp/custom_pages/service_bottom.htm
  11. 25
      clientsideapp/templates/clientsideapp/debts.html
  12. 51
      clientsideapp/templates/clientsideapp/ext.html
  13. 71
      clientsideapp/templates/clientsideapp/index.html
  14. 6
      clientsideapp/templates/clientsideapp/modal_service_buy.html
  15. 44
      clientsideapp/templates/clientsideapp/services.html
  16. 21
      devapp/base_intr.py
  17. 28
      devapp/dev_types.py
  18. 2
      devapp/models.py
  19. 2
      devapp/onu_register.sh
  20. 8
      devapp/templates/devapp/custom_dev_page/ports.html
  21. 27
      devapp/views.py
  22. 69
      djing/global_base_views.py
  23. 2
      djing/local_settings.py.template
  24. 6
      djing/settings.py
  25. 0
      djing/templatetags/__init__.py
  26. 9
      djing/templatetags/globaltags.py
  27. 3
      mapapp/templates/maps/dot.html
  28. 4
      mapapp/templates/maps/modal_add_dot.html
  29. 359
      migrate_to_0.2.py
  30. 32
      queue_mngr.py
  31. BIN
      static/clientside/bc.png
  32. 4
      static/clientside/custom.css
  33. BIN
      static/img/bcgr.png
  34. 15
      static/js/my.js
  35. 2
      systemd_units/djing.service
  36. 2
      systemd_units/djing_queue.service
  37. 2
      systemd_units/djing_rotate.service
  38. 2
      systemd_units/djing_telebot.service
  39. 23
      systemd_units/webdav_backup.py
  40. 4
      taskapp/locale/ru/LC_MESSAGES/django.po
  41. 3
      taskapp/templates/taskapp/add_edit_task.html
  42. 15
      taskapp/templates/taskapp/tasklist.html
  43. 3
      taskapp/templates/taskapp/tasklist_all.html
  44. 10
      taskapp/templates/taskapp/tasklist_failed.html
  45. 2
      taskapp/templates/taskapp/tasklist_own.html
  46. 5
      taskapp/views.py
  47. 6
      templates/all_base.html

12
abonapp/templates/abonapp/peoples.html

@ -63,7 +63,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% with can_ch_trf=perms.tariff_app.change_tariff can_del_trf=perms.abonapp.delete_abon %}
{% with can_ch_trf=perms.tariff_app.change_tariff can_del_abon=perms.abonapp.delete_abon %}
{% for human in peoples %} {% for human in peoples %}
{% if human.is_active %} {% if human.is_active %}
<tr> <tr>
@ -94,11 +94,11 @@
<td>{{ human.house|default:'-' }}</td> <td>{{ human.house|default:'-' }}</td>
<td><a href="tel:{{ human.telephone }}">{{ human.telephone }}</a></td> <td><a href="tel:{{ human.telephone }}">{{ human.telephone }}</a></td>
<td> <td>
{% if human.active_tariff %}
{% if perms.tariff_app.change_tariff %}
<a href="{% url 'tarifs:edit' human.active_tariff.tariff.pk %}">{{ human.active_tariff.tariff.title }}</a>
{% if human.current_tariff %}
{% if can_ch_trf %}
<a href="{% url 'tarifs:edit' human.current_tariff.tariff.pk %}">{{ human.current_tariff.tariff.title }}</a>
{% else %} {% else %}
{{ human.active_tariff.tariff.title }}
{{ human.current_tariff.tariff.title }}
{% endif %} {% endif %}
{% else %}&mdash;&mdash;&mdash; {% else %}&mdash;&mdash;&mdash;
{% endif %} {% endif %}
@ -109,7 +109,7 @@
{% endfor %} {% endfor %}
</td> </td>
<td class="hidden-xs"> <td class="hidden-xs">
{% if can_del_trf %}
{% if can_del_abon %}
<a href="{% url 'abonapp:del_abon' %}?id={{ human.pk }}" class="btn btn-danger btn-sm"> <a href="{% url 'abonapp:del_abon' %}?id={{ human.pk }}" class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-remove"></span> <span class="glyphicon glyphicon-remove"></span>
</a> </a>

17
abonapp/views.py

@ -6,7 +6,7 @@ from django.db import IntegrityError, ProgrammingError, transaction
from django.db.models import Count, Q from django.db.models import Count, Q
from django.shortcuts import render, redirect, get_object_or_404, resolve_url from django.shortcuts import render, redirect, get_object_or_404, resolve_url
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
from django.http import HttpResponse, Http404
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -27,13 +27,13 @@ from statistics.models import getModel
from group_app.models import Group 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.global_base_views import OrderingMixin
from djing.global_base_views import OrderingMixin, BaseListWithFiltering
PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
@method_decorator([login_required, mydefs.only_admins], name='dispatch') @method_decorator([login_required, mydefs.only_admins], name='dispatch')
class BaseAbonListView(ListView, OrderingMixin):
class BaseAbonListView(OrderingMixin, BaseListWithFiltering):
paginate_by = PAGINATION_ITEMS_PER_PAGE paginate_by = PAGINATION_ITEMS_PER_PAGE
http_method_names = ['get'] http_method_names = ['get']
@ -45,7 +45,7 @@ class PeoplesListView(BaseAbonListView):
def get_queryset(self): def get_queryset(self):
street_id = mydefs.safe_int(self.request.GET.get('street')) street_id = mydefs.safe_int(self.request.GET.get('street'))
gid = mydefs.safe_int(self.kwargs.get('gid')) gid = mydefs.safe_int(self.kwargs.get('gid'))
peoples_list = models.Abon.objects.select_related('group', 'street')
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)
else: else:
@ -60,7 +60,11 @@ class PeoplesListView(BaseAbonListView):
pass pass
except mydefs.LogicError as e: except mydefs.LogicError as e:
messages.warning(self.request, e) messages.warning(self.request, e)
ordering = self.get_ordering()
if ordering:
if isinstance(ordering, str):
ordering = (ordering,)
peoples_list = peoples_list.order_by(*ordering)
return peoples_list return peoples_list
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -545,7 +549,8 @@ def clear_dev(request, gid, uid):
try: try:
abon = models.Abon.objects.get(pk=uid) abon = models.Abon.objects.get(pk=uid)
abon.device = None abon.device = None
abon.save(update_fields=['device'])
abon.dev_port = None
abon.save(update_fields=['device', 'dev_port'])
messages.success(request, _('Device has successfully unattached')) messages.success(request, _('Device has successfully unattached'))
except models.Abon.DoesNotExist: except models.Abon.DoesNotExist:
messages.error(request, _('Abon does not exist')) messages.error(request, _('Abon does not exist'))

2
accounts_app/views.py

@ -213,7 +213,7 @@ def perms(request, uid):
klasses = ( klasses = (
'abonapp.Abon', 'accounts_app.UserProfile', 'abonapp.Abon', 'accounts_app.UserProfile',
'abonapp.AbonTariff', 'abonapp.AbonStreet', 'devapp.Device', 'abonapp.AbonTariff', 'abonapp.AbonStreet', 'devapp.Device',
'abonapp.PassportInfo', 'abonapp.AdditionalTelephone'
'abonapp.PassportInfo', 'abonapp.AdditionalTelephone', 'tariff_app.PeriodicPay'
) )
return render(request, 'accounts/perms/objects_types.html', { return render(request, 'accounts/perms/objects_types.html', {
'userprofile': userprofile, 'userprofile': userprofile,

5
agent/netflow/netflow_handler.py

@ -4,6 +4,7 @@ import sys
import os import os
from importlib import import_module from importlib import import_module
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("File name of netflow required") print("File name of netflow required")
@ -48,8 +49,8 @@ if __name__ == '__main__':
db.close() db.close()
os.system( os.system(
'/usr/bin/bash -c '
'"%(CUR_DIR)s/djing_flow %(TMP_IPUSER_FILE)s < %(TMP_DUMP)s | '
'bash -c "export LD_LIBRARY_PATH=. && '
'%(CUR_DIR)s/djing_flow %(TMP_IPUSER_FILE)s < %(TMP_DUMP)s | '
'/usr/bin/mysql -u%(DB_USER)s -h %(HOST)s -p %(DB_NAME)s --password=%(DB_PASSW)s"' % { '/usr/bin/mysql -u%(DB_USER)s -h %(HOST)s -p %(DB_NAME)s --password=%(DB_PASSW)s"' % {
'CUR_DIR': cur_dir, 'CUR_DIR': cur_dir,
'TMP_IPUSER_FILE': tmp_ipuser_file, 'TMP_IPUSER_FILE': tmp_ipuser_file,

2
chatbot/models.py

@ -57,7 +57,7 @@ class MessageQueue(models.Model):
('r', 'Read') ('r', 'Read')
) )
status = models.CharField(_('Status of message'), max_length=1, choices=STATUSES, default='n') status = models.CharField(_('Status of message'), max_length=1, choices=STATUSES, default='n')
# tag каждое приложение ставит своим чтоб делить сообщения между этими приложениями
# tag: each application puts its own to separate messages between these applications
tag = models.CharField(_('App tag'), max_length=6, default='none') tag = models.CharField(_('App tag'), max_length=6, default='none')
objects = MessageQueueManager() objects = MessageQueueManager()

143
clientsideapp/locale/ru/LC_MESSAGES/django.po

@ -8,7 +8,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-03-18 00:15+0300\n"
"POT-Creation-Date: 2018-03-20 01:51+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"
@ -54,6 +54,91 @@ msgstr "Подтвердить"
msgid "Cancel" msgid "Cancel"
msgstr "Отменить" msgstr "Отменить"
#: templates/clientsideapp/debts.html:6
msgid "Your debt"
msgstr "Ваши долги"
#: templates/clientsideapp/debts.html:11
msgid "State"
msgstr "Состояние"
#: templates/clientsideapp/debts.html:12
msgid "Summ"
msgstr "Сумма"
#: templates/clientsideapp/debts.html:13
msgid "Description"
msgstr "Описание"
#: templates/clientsideapp/debts.html:14
msgid "Date of create"
msgstr "Дата создания"
#: templates/clientsideapp/debts.html:15
msgid "Date of pay"
msgstr "Дата платежа"
#: templates/clientsideapp/debts.html:16 templates/clientsideapp/debts.html:38
msgid "Pay"
msgstr "Оплатить"
#: templates/clientsideapp/debts.html:23 templates/clientsideapp/ext.html:63
#: templates/clientsideapp/modal_service_buy.html:19
#: templates/clientsideapp/services.html:26
#: templates/clientsideapp/services.html:55
msgid "currency"
msgstr "руб."
#: templates/clientsideapp/debts.html:31
msgid "Created paid"
msgstr "Создан оплаченным"
#: templates/clientsideapp/debts.html:33
msgid "Not yet paid"
msgstr "Ещё не оплачен"
#: templates/clientsideapp/debts.html:45
msgid "You have no debt"
msgstr "У вас нет долгов"
#: templates/clientsideapp/ext.html:7 templates/clientsideapp/ext.html:41
msgid "Personal account"
msgstr "Личный кабинет"
#: templates/clientsideapp/ext.html:46
msgid "Pays"
msgstr "Платежи"
#: templates/clientsideapp/ext.html:51
msgid "Services"
msgstr "Услуги"
#: templates/clientsideapp/ext.html:55
msgid "Other"
msgstr "Другое"
#: templates/clientsideapp/ext.html:57
msgid "Show debts and pay it"
msgstr "Посмотреть долги и оплатить"
#: templates/clientsideapp/ext.html:58
msgid "Quit"
msgstr "Выйти"
#: templates/clientsideapp/ext.html:63
#, python-format
msgid "Your balance is <b>%(ballance)s</b>"
msgstr "Ваш балланс <b>%(ballance)s</b>"
#: templates/clientsideapp/ext.html:74
msgid ""
"<strong>Attantion!</strong> You are is admin, and do not be active here, "
"please back to admin side. Client side to you for reference only."
msgstr ""
"<strong>Кстати.</strong> Вы администратор, и не должны производить действия "
"из кабинета пользователя, производите их из админки. Кабинет клиента для вас "
"только для ознакомления."
#: templates/clientsideapp/modal_service_buy.html:5 #: templates/clientsideapp/modal_service_buy.html:5
msgid "Pick service" msgid "Pick service"
msgstr "Заказать услугу" msgstr "Заказать услугу"
@ -62,20 +147,32 @@ msgstr "Заказать услугу"
msgid "Are you sure you want to order the service?" msgid "Are you sure you want to order the service?"
msgstr "Вы уверены что хотите заказать услугу?" msgstr "Вы уверены что хотите заказать услугу?"
#: templates/clientsideapp/modal_service_buy.html:9
msgid ""
"Be careful, after purchasing the service you will <b>withdraw money</b>, and "
"you will be able to use the purchased service."
msgstr ""
"Будте внимательны, после заказа услуги с вашего счёта <b>снимутся средства</"
"b>, и вы сможете пользоваться купленной услугой."
#: templates/clientsideapp/modal_service_buy.html:15 #: templates/clientsideapp/modal_service_buy.html:15
#, python-format #, python-format
msgid "" msgid ""
"Inbound speed: %(speedIn)s MBit/s<br>\n"
"Outgoing speed: %(speedOut)s MBit/s<br>\n"
"Cost: %(amount)s rubles."
"Inbound speed: %(speedIn)s MBit/s<br> Outgoing speed: %(speedOut)s MBit/"
"s<br> Cost: %(amount)s rubles."
msgstr "" msgstr ""
#: templates/clientsideapp/modal_service_buy.html:22
#: templates/clientsideapp/services.html:59
#: templates/clientsideapp/modal_service_buy.html:19
#, python-format
msgid "The cost is %(amount)s"
msgstr "Стоимость %(amount)s"
#: templates/clientsideapp/modal_service_buy.html:23
#: templates/clientsideapp/services.html:63
msgid "Pick" msgid "Pick"
msgstr "Заказать" msgstr "Заказать"
#: templates/clientsideapp/modal_service_buy.html:24
#: templates/clientsideapp/modal_service_buy.html:25
msgid "Close" msgid "Close"
msgstr "Закрыть" msgstr "Закрыть"
@ -99,9 +196,37 @@ msgstr "Комментарий"
msgid "You have not spent payments" msgid "You have not spent payments"
msgstr "У вас нет проведённых платежей" msgstr "У вас нет проведённых платежей"
#: templates/clientsideapp/services.html:14
msgid "Your current service"
msgstr "Ваша текущая услуга"
#: templates/clientsideapp/services.html:19
msgid "The date of connection"
msgstr "Дата подключения"
#: templates/clientsideapp/services.html:22
msgid "The date of finish service"
msgstr "Дата завершения услуги"
#: templates/clientsideapp/services.html:25 #: templates/clientsideapp/services.html:25
msgid "currency"
msgstr "руб."
msgid "Cost"
msgstr "Стоимость"
#: templates/clientsideapp/services.html:35
msgid ""
"<strong>Attantion!</strong> You have not yet a service, for use the services "
"please purchase service you want."
msgstr ""
"<strong>Внимание!</strong> У вас нет услуги, для использования ресурсов "
"приобретите нужную услугу из представленных тут."
#: templates/clientsideapp/services.html:46
msgid "Services available for ordering"
msgstr "Доступные для заказа услуги"
#: templates/clientsideapp/services.html:68
msgid "No services available for ordering"
msgstr "Нет доступных для заказа услуг"
#: views.py:51 #: views.py:51
#, python-format #, python-format

1
clientsideapp/templates/clientsideapp/custom_pages/footer.htm

@ -0,0 +1 @@
Your custom content <i>here</i>.

1
clientsideapp/templates/clientsideapp/custom_pages/main_page.htm

@ -0,0 +1 @@
<h1>You can change this page</h1>

2
clientsideapp/templates/clientsideapp/custom_pages/service.htm

@ -0,0 +1,2 @@
<h1>Your custom content</h1>
<p>You have service variable {{ active_service }}</p>

1
clientsideapp/templates/clientsideapp/custom_pages/service_bottom.htm

@ -0,0 +1 @@
<h1>Your custom content on bottom</h1>

25
clientsideapp/templates/clientsideapp/debts.html

@ -1,25 +1,26 @@
{% extends 'clientsideapp/ext.html' %} {% extends 'clientsideapp/ext.html' %}
{% load i18n %}
{% block client_main %} {% block client_main %}
<div class="page-header"> <div class="page-header">
<h3>Ваши долги</h3>
<h3>{% trans 'Your debt' %}</h3>
</div> </div>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th class="col-xs-1">Состояние</th>
<th>Сумма</th>
<th>Описание</th>
<th>Дата создания</th>
<th>Дата платежа</th>
<th class="col-xs-1">Оплатить</th>
<th class="col-xs-1">{% trans 'State' %}</th>
<th>{% trans 'Summ' %}</th>
<th>{% trans 'Description' %}</th>
<th>{% trans 'Date of create' %}</th>
<th>{% trans 'Date of pay' %}</th>
<th class="col-xs-1">{% trans 'Pay' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for debt in debts %} {% for debt in debts %}
<tr> <tr>
<td class="text-center"><span class="glyphicon glyphicon-{{ debt.status|yesno:'ok,time' }}"></span></td> <td class="text-center"><span class="glyphicon glyphicon-{{ debt.status|yesno:'ok,time' }}"></span></td>
<td>{{ debt.amount }} руб.</td>
<td>{{ debt.amount }} {% trans 'currency' %}</td>
<td>{{ debt.comment }}</td> <td>{{ debt.comment }}</td>
<td>{{ debt.date_create|date:'d b H:i' }}</td> <td>{{ debt.date_create|date:'d b H:i' }}</td>
<td> <td>
@ -27,21 +28,21 @@
{{ debt.date_pay|date:'d b H:i' }} {{ debt.date_pay|date:'d b H:i' }}
{% else %} {% else %}
{% if debt.status %} {% if debt.status %}
Создан оплаченным
{% trans 'Created paid' %}
{% else %} {% else %}
Ещё не оплачен
{% trans 'Not yet paid' %}
{% endif %} {% endif %}
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">
<a href="{% url 'client_side:debt_buy' debt.id %}" class="btn btn-sm btn-success" title="Оплатить">
<a href="{% url 'client_side:debt_buy' debt.id %}" class="btn btn-sm btn-success" title="{% trans 'Pay' %}">
<span class="glyphicon glyphicon-usd"></span> <span class="glyphicon glyphicon-usd"></span>
</a> </a>
</td> </td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td colspan="6">У вас нет долгов <span class="glyphicon glyphicon-ok"></span></td>
<td colspan="6">{% trans 'You have no debt' %} <span class="glyphicon glyphicon-ok"></span></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

51
clientsideapp/templates/clientsideapp/ext.html

@ -1,10 +1,10 @@
<!DOCTYPE HTML>
<!DOCTYPE HTML>{% load globaltags %}{% load i18n %}
<html lang="ru"> <html lang="ru">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Личный кабинет</title>
<title>{% global_var 'COMPANY_NAME' %} - {% trans 'Personal account' %}</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet"> <link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/clientside/custom.css" rel="stylesheet"> <link href="/static/clientside/custom.css" rel="stylesheet">
<script src="/static/js/jquery-2.2.4.min.js"></script> <script src="/static/js/jquery-2.2.4.min.js"></script>
@ -19,6 +19,8 @@
<div class="modal-content" id="modContent"></div> <div class="modal-content" id="modContent"></div>
</div> </div>
</div> </div>
{% url 'client_side:home' as client_side_home %}
<!-- Fixed navbar --> <!-- Fixed navbar -->
<div class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container"> <div class="container">
@ -30,48 +32,36 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<img src="/static/clientside/bc.png" class="navbar-brand"> <img src="/static/clientside/bc.png" class="navbar-brand">
<a class="navbar-brand hidden-xs" href="{{ client_side_home }}">{% global_var 'COMPANY_NAME' %}</a>
</div> </div>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
{% url 'client_side:home' as client_side_home %}
<li{% if client_side_home == request.path %} class="active"{% endif %}> <li{% if client_side_home == request.path %} class="active"{% endif %}>
<a href="{{ client_side_home }}">Личный кабинет</a>
<a href="{{ client_side_home }}">{% trans 'Personal account' %}</a>
</li> </li>
{% url 'client_side:pays' as client_side_pays %} {% url 'client_side:pays' as client_side_pays %}
<li{% if client_side_pays == request.path %} class="active"{% endif %}> <li{% if client_side_pays == request.path %} class="active"{% endif %}>
<a href="{{ client_side_pays }}">Платежи</a>
<a href="{{ client_side_pays }}">{% trans 'Pays' %}</a>
</li> </li>
{% url 'client_side:services' as client_side_services %} {% url 'client_side:services' as client_side_services %}
<li{% if client_side_services == request.path %} class="active"{% endif %}> <li{% if client_side_services == request.path %} class="active"{% endif %}>
<a href="{{ client_side_services }}">Услуги</a>
</li>
<!--<li>
<a href="#">
Сообщения из администрации
<span class="badge">14</span>
</a>
<a href="{{ client_side_services }}">{% trans 'Services' %}</a>
</li> </li>
<li><a href="#contact">График трафика</a></li>-->
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Другое <b class="caret"></b></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{% trans 'Other' %} <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<!--<li><a href="#">Подать заявку</a></li>-->
<li><a href="{% url 'client_side:debts' %}">Просмотреть и оплатить долги</a></li>
<!--<li><a href="#">Сеансы подключений</a></li>
<li class="divider"></li>
<li class="dropdown-header">Nav header</li>
<li><a href="#">Separated link</a></li>-->
<li><a href="{% url 'acc_app:logout' %}">Выйти</a></li>
<li><a href="{% url 'client_side:debts' %}">{% trans 'Show debts and pay it' %}</a></li>
<li><a href="{% url 'acc_app:logout' %}">{% trans 'Quit' %}</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
<span class="navbar-text">Ваш балланс <b>{{ request.user.ballance|floatformat:2 }}</b> руб.</span>
<span class="navbar-text">
{% blocktrans with ballance=request.user.ballance|floatformat:2 %}Your balance is <b>{{ ballance }}</b>{% endblocktrans %} {% trans 'currency' %}
</span>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>
</div> </div>
@ -80,11 +70,12 @@
<div class="container"> <div class="container">
{% if request.user.is_staff %} {% if request.user.is_staff %}
<div class="alert alert-info">
<strong>Кстати.</strong>
Вы администратор, и не должны производить действия из кабинета пользователя, производите их из админки.
Кабинет клиента для вас только для ознакомления.
</div>
<div class="alert alert-info">
{% blocktrans trimmed %}
<strong>Attantion!</strong>
You are is admin, and do not be active here, please back to admin side. Client side to you for reference only.
{% endblocktrans %}
</div>
{% endif %} {% endif %}
{% include 'message_block.html' %} {% include 'message_block.html' %}
@ -95,7 +86,7 @@
<div id="footer"> <div id="footer">
<div class="container"> <div class="container">
<p class="text-muted"> <p class="text-muted">
Напишите разработчику <i>is-ttk@ya.ru</i>.
{% include 'clientsideapp/custom_pages/footer.htm' %}
</p> </p>
</div> </div>
</div> </div>

71
clientsideapp/templates/clientsideapp/index.html

@ -1,73 +1,4 @@
{% extends 'clientsideapp/ext.html' %} {% extends 'clientsideapp/ext.html' %}
{% block client_main %} {% block client_main %}
<div class="panel panel-primary">
<div class="panel-heading">Наши реквизиты</div>
<div class="panel-body">
<ul>
<li><b>Режим работы:</b> с 9:00 до 22:00</li>
<li><b>Телефоны:</b> +79788328885, +79788318999</li>
<li><b>Адрес:</b> пос. Нижнегорский, ул. Победы 38. 2й этаж старого дома быта, возле рынка</li>
</ul>
<script type="text/javascript" charset="utf-8" async src="https://api-maps.yandex.ru/services/constructor/1.0/js/?um=constructor%3A62221c38a606ad870f2cabc9fd476c8ae1a532993915b73553693f7fb5c7de68&amp;width=100%25&amp;height=654&amp;lang=ru_RU&amp;scroll=true"></script>
</div>
</div>
<div class="page-header">
<h3 class="page-header">Тарифы на дополнительные услуги</h3>
<!--Скачать <a href="/static/clientside/ISPlaylist.m3u">IPTV список каналов</a>, кодировка UTF-8</p>-->
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Услуга</th>
<th>Стоимость (руб)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Выезд мастера. Цена зависит от удалённости от Нижнегорска</td>
<td>100-200</td>
</tr>
<tr>
<td>Настройка роутера</td>
<td>300</td>
</tr>
<tr>
<td>Установка и восстановление сетевых настроек</td>
<td>100</td>
</tr>
<tr>
<td>Замена кабеля (витая пара)</td>
<td>30 руб/метр</td>
</tr>
<tr>
<td>Замена конектора <a href="/static/clientside/rj45.png" target="_blank">RJ-45</a></td>
<td>50</td>
</tr>
<tr>
<td>Роутер <a href="/static/clientside/TL-WR840N-v2.jpg" target="_blank">TL-WR840N</a> + настройка</td>
<td>1800</td>
</tr>
<tr>
<td>Установка драйвера (с настройкой сетевых установок)</td>
<td>100</td>
</tr>
<tr>
<td>"Что-то не работает" &#8211; по договорённости</td>
<td>~</td>
</tr>
<tr>
<td>Замена блока питания</td>
<td>200</td>
</tr>
<tr>
<td>Замена оптоволоконных линий связи</td>
<td>от 500</td>
</tr>
</tbody>
</table>
</div>
{% include 'clientsideapp/custom_pages/main_page.htm' %}
{% endblock %} {% endblock %}

6
clientsideapp/templates/clientsideapp/modal_service_buy.html

@ -6,7 +6,7 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<h3>{% trans 'Are you sure you want to order the service?' %}</h3> <h3>{% trans 'Are you sure you want to order the service?' %}</h3>
<p>Будте внимательны, после заказа услуги с вашего счёта <b>снимутся средства</b>, и вы сможете пользоваться купленной услугой.</p>
<p>{% trans 'Be careful, after purchasing the service you will <b>withdraw money</b>, and you will be able to use the purchased service.' %}</p>
<h3>{{ service.title }}</h3> <h3>{{ service.title }}</h3>
@ -15,8 +15,8 @@
<!--<p>{# {% blocktrans trimmed with speedIn=service.speedIn speedOut=service.speedOut amount=service.amount %} <!--<p>{# {% blocktrans trimmed with speedIn=service.speedIn speedOut=service.speedOut amount=service.amount %}
Inbound speed: {{ speedIn }} MBit/s<br> Inbound speed: {{ speedIn }} MBit/s<br>
Outgoing speed: {{ speedOut }} MBit/s<br> Outgoing speed: {{ speedOut }} MBit/s<br>
Cost: {{ amount }} rubles.{% endblocktrans %} #}</p>-->
<p>Стоимость {{ service.amount }} руб.</p>
Cost: {{ amount }} rubles.{% endblocktrans %}</p>-->
<p>{% blocktrans with amount=service.amount|floatformat:2 %}The cost is {{ amount }}{% endblocktrans %} {% trans 'currency' %}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-sm btn-primary"> <button type="submit" class="btn btn-sm btn-primary">

44
clientsideapp/templates/clientsideapp/services.html

@ -8,38 +8,42 @@
<div class="row"> <div class="row">
<div class="col-lg-7"> <div class="col-lg-7">
{% if current_service %} {% if current_service %}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
Ваша текущая услуга
{% trans 'Your current service' %}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<h3 class="panel-title">{{ current_service.tariff.title }}</h3><br> <h3 class="panel-title">{{ current_service.tariff.title }}</h3><br>
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>Дата подключения</dt>
<dt>{% trans 'The date of connection' %}</dt>
<dd>{{ current_service.time_start|date:"d E Y, l" }}</dd> <dd>{{ current_service.time_start|date:"d E Y, l" }}</dd>
<dt>Время завершения услуги</dt>
<dt>{% trans 'The date of finish service' %}</dt>
<dd>{{ current_service.deadline|date:"d E Y, l" }}</dd> <dd>{{ current_service.deadline|date:"d E Y, l" }}</dd>
<dt>Стоимость</dt>
<dt>{% trans 'Cost' %}</dt>
<dd>{{ current_service.tariff.amount }} {% trans 'currency' %}</dd> <dd>{{ current_service.tariff.amount }} {% trans 'currency' %}</dd>
</dl> </dl>
<p>{{ current_service.tariff.descr }}</p> <p>{{ current_service.tariff.descr }}</p>
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
<span class="glyphicon glyphicon-exclamation-sign"></span> <span class="glyphicon glyphicon-exclamation-sign"></span>
<strong>Внимание!</strong> У вас нет услуги, для использования ресурсов приобретите нужную услугу из представленных тут.
{% blocktrans trimmed %}
<strong>Attantion!</strong>
You have not yet a service, for use the services please purchase service you want.
{% endblocktrans %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="col-lg-5"> <div class="col-lg-5">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
Доступные для заказа услуги
{% trans 'Services available for ordering' %}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div class="container-fluid"> <div class="container-fluid">
@ -48,7 +52,7 @@
<div class="col-lg-12"> <div class="col-lg-12">
<h3>{{ tarif.title }}</h3> <h3>{{ tarif.title }}</h3>
<i>{{ tarif.amount }} руб.</i>
<i>{{ tarif.amount }} {% trans 'currency' %}</i>
<p>{{ tarif.descr }}</p> <p>{{ tarif.descr }}</p>
{% if request.user.is_staff or current_service %} {% if request.user.is_staff or current_service %}
@ -61,7 +65,7 @@
</div> </div>
{% empty %} {% empty %}
<div class="col-lg-4"> <div class="col-lg-4">
<h3 class="panel-title">Нет доступных услуг для заказа</h3>
<h3 class="panel-title">{% trans 'No services available for ordering' %}</h3>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -70,28 +74,10 @@
</div> </div>
</div> </div>
</div> </div>
{% include 'clientsideapp/services_ext.txt' %}
{% include 'clientsideapp/custom_pages/service.htm' with active_service=current_service %}
</div> </div>
</div> </div>
{% include 'clientsideapp/custom_pages/service_bottom.htm' %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-info">
<div class="panel-heading">
<span class="glyphicon glyphicon-info-sign"></span>
Как работает личный кабинет, раздел услуг
</div>
<div class="panel-body">
<p>Перед вами находится 2 столбца, в левом ваша текущая подключённая услуга, а в правом доступные для заказа услуги.
Когда у вас уже есть подключённая услуга то заказать новую вы не можете пока не завершится текущая.</p>
<p>Когда у вас нет действующей подключённой услуги то кнопка заказа станет активной, и вы сможете заказать для себя услугу.
Обратите внимание что именно в этот момент с вашего счёта снимутся деньги в соответствии со стоимостью услуги.
</p>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

21
devapp/base_intr.py

@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from easysnmp import Session from easysnmp import Session
class DeviceImplementationError(Exception):
pass
class DevBase(object, metaclass=ABCMeta): class DevBase(object, metaclass=ABCMeta):
def __init__(self, dev_instance=None): def __init__(self, dev_instance=None):
@ -10,19 +13,19 @@ class DevBase(object, metaclass=ABCMeta):
@staticmethod @staticmethod
def description(): def description():
"""Возвращает текстовое описание"""
pass
@abstractmethod @abstractmethod
def reboot(self): def reboot(self):
"""Перезагружает устройство"""
pass
@abstractmethod @abstractmethod
def get_ports(self): def get_ports(self):
"""Получаем инфу о портах"""
pass
@abstractmethod @abstractmethod
def get_device_name(self): def get_device_name(self):
"""Получаем имя устройства по snmp"""
"""Return device name by snmp"""
@abstractmethod @abstractmethod
def uptime(self): def uptime(self):
@ -30,17 +33,17 @@ class DevBase(object, metaclass=ABCMeta):
@abstractmethod @abstractmethod
def get_template_name(self): def get_template_name(self):
"""Получаем путь к html шаблону отображения устройства"""
"""Return path to html template for device"""
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def has_attachable_to_subscriber(): def has_attachable_to_subscriber():
"""Можно-ли подключать устройство к абоненту"""
"""Can connect device to subscriber"""
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def is_use_device_port(): def is_use_device_port():
"""True если при авторизации по opt82 используется порт"""
"""True if used device port while opt82 authorization"""
class BasePort(object, metaclass=ABCMeta): class BasePort(object, metaclass=ABCMeta):
@ -70,7 +73,7 @@ class SNMPBaseWorker(object, metaclass=ABCMeta):
self.ses = Session(hostname=ip, community=community, version=ver) self.ses = Session(hostname=ip, community=community, version=ver)
def set_int_value(self, oid, value): def set_int_value(self, oid, value):
return self.ses.set(oid, value)
return self.ses.set(oid, value, 'i')
def get_list(self, oid): def get_list(self, oid):
for v in self.ses.walk(oid): for v in self.ses.walk(oid):

28
devapp/dev_types.py

@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
from mydefs import RuTimedelta, safe_int from mydefs import RuTimedelta, safe_int
from datetime import timedelta from datetime import timedelta
from easysnmp import EasySNMPTimeoutError from easysnmp import EasySNMPTimeoutError
from .base_intr import DevBase, SNMPBaseWorker, BasePort
from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError
class DLinkPort(BasePort): class DLinkPort(BasePort):
@ -41,21 +41,25 @@ class DLinkDevice(DevBase, SNMPBaseWorker):
return self.get_item('.1.3.6.1.4.1.2021.8.1.101.1') return self.get_item('.1.3.6.1.4.1.2021.8.1.101.1')
def get_ports(self): def get_ports(self):
interfaces_count = safe_int(self.get_item('.1.3.6.1.2.1.2.1.0'))
nams = list(self.get_list('.1.3.6.1.4.1.171.10.134.2.1.1.100.2.1.3')) nams = list(self.get_list('.1.3.6.1.4.1.171.10.134.2.1.1.100.2.1.3'))
stats = list(self.get_list('.1.3.6.1.2.1.2.2.1.7')) stats = list(self.get_list('.1.3.6.1.2.1.2.2.1.7'))
macs = list(self.get_list('.1.3.6.1.2.1.2.2.1.6')) macs = list(self.get_list('.1.3.6.1.2.1.2.2.1.6'))
speeds = self.get_list('.1.3.6.1.2.1.31.1.1.1.15')
speeds = list(self.get_list('.1.3.6.1.2.1.2.2.1.5'))
res = [] res = []
for n, speed in enumerate(speeds):
status = True if int(stats[n]) == 1 else False
res.append(DLinkPort(
n+1,
nams[n] if len(nams) > 0 else _('does not fetch the name'),
status,
macs[n] if len(macs) > 0 else _('does not fetch the mac'),
int(speed or 0),
self))
return res
try:
for n in range(interfaces_count):
status = True if int(stats[n]) == 1 else False
res.append(DLinkPort(
n+1,
nams[n] if len(nams) > 0 else '',
status,
macs[n] if len(macs) > 0 else _('does not fetch the mac'),
int(speeds[n]) if len(speeds) > 0 else 0,
self))
return res
except IndexError:
raise DeviceImplementationError('Dlink port index error')
def get_device_name(self): def get_device_name(self):
return self.get_item('.1.3.6.1.2.1.1.1.0') return self.get_item('.1.3.6.1.2.1.1.1.0')

2
devapp/models.py

@ -76,7 +76,7 @@ class Device(models.Model):
) )
verbose_name = _('Device') verbose_name = _('Device')
verbose_name_plural = _('Devices') verbose_name_plural = _('Devices')
ordering = ['comment']
ordering = ['id']
def get_abons(self): def get_abons(self):
pass pass

2
devapp/onu_register.sh

@ -29,6 +29,6 @@ if grep "${MAC}" "${DHCP_PATH}/${PART_CODE}.conf" > /dev/null; then
else else
# add new mac # add new mac
echo "subclass \"${PART_CODE}\" \"${MAC}\";" >> "${DHCP_PATH}/${PART_CODE}.conf" echo "subclass \"${PART_CODE}\" \"${MAC}\";" >> "${DHCP_PATH}/${PART_CODE}.conf"
/usr/bin/sudo /usr/bin/systemctl restart dhcpd.service
sudo systemctl restart isc-dhcp-server.service
fi fi

8
devapp/templates/devapp/custom_dev_page/ports.html

@ -18,16 +18,16 @@
{% for port in ports %} {% for port in ports %}
{% if port.st %} {% if port.st %}
{% if port.sp == 10 %}
{% if port.sp == 10000000 %}
<div class="port kilo text-center"> <div class="port kilo text-center">
<b>10 mbps</b> <b>10 mbps</b>
{% elif port.sp == 100 %}
{% elif port.sp == 100000000 %}
<div class="port mega text-center"> <div class="port mega text-center">
<b>100 mbps</b> <b>100 mbps</b>
{% elif port.sp == 1000 %}
{% elif port.sp == 1000000000 %}
<div class="port giga text-center"> <div class="port giga text-center">
<b>1 gbps</b> <b>1 gbps</b>
{% elif port.sp == 10000 %}
{% elif port.sp == 10000000000 %}
<div class="port ten text-center"> <div class="port ten text-center">
<b>10 gbps</b> <b>10 gbps</b>
{% else %} {% else %}

27
devapp/views.py

@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _, gettext
from easysnmp import EasySNMPTimeoutError, EasySNMPError from easysnmp import EasySNMPTimeoutError, EasySNMPError
from django.views.generic import ListView, DetailView from django.views.generic import ListView, DetailView
from devapp.base_intr import DeviceImplementationError
from mydefs import res_success, res_error, only_admins, ping, ip_addr_regex from mydefs import res_success, res_error, only_admins, ping, ip_addr_regex
from abonapp.models import Abon from abonapp.models import Abon
from group_app.models import Group from group_app.models import Group
@ -23,7 +24,7 @@ from guardian.shortcuts import get_objects_for_user
from chatbot.telebot import send_notify from chatbot.telebot import send_notify
from chatbot.models import ChatException from chatbot.models import ChatException
from jsonview.decorators import json_view from jsonview.decorators import json_view
from djing.global_base_views import HashAuthView, AllowedSubnetMixin, OrderingMixin
from djing import global_base_views
from .models import Device, Port, DeviceDBException, DeviceMonitoringException from .models import Device, Port, DeviceDBException, DeviceMonitoringException
from .forms import DeviceForm, PortForm from .forms import DeviceForm, PortForm
from mydefs import safe_int from mydefs import safe_int
@ -35,7 +36,7 @@ class BaseDeviceListView(ListView):
@method_decorator([login_required, only_admins], name='dispatch') @method_decorator([login_required, only_admins], name='dispatch')
class DevicesListView(BaseDeviceListView, OrderingMixin):
class DevicesListView(BaseDeviceListView, global_base_views.OrderingMixin):
context_object_name = 'devices' context_object_name = 'devices'
template_name = 'devapp/devices.html' template_name = 'devapp/devices.html'
@ -62,7 +63,7 @@ class DevicesListView(BaseDeviceListView, OrderingMixin):
@method_decorator([login_required, only_admins], name='dispatch') @method_decorator([login_required, only_admins], name='dispatch')
class DevicesWithoutGroupsListView(BaseDeviceListView, OrderingMixin):
class DevicesWithoutGroupsListView(BaseDeviceListView, global_base_views.OrderingMixin):
context_object_name = 'devices' context_object_name = 'devices'
template_name = 'devapp/devices_null_group.html' template_name = 'devapp/devices_null_group.html'
queryset = Device.objects.filter(group=None).only('comment', 'devtype', 'pk', 'ip_address') queryset = Device.objects.filter(group=None).only('comment', 'devtype', 'pk', 'ip_address')
@ -179,7 +180,7 @@ def manage_ports(request, device_id):
@method_decorator([login_required, only_admins], name='dispatch') @method_decorator([login_required, only_admins], name='dispatch')
class ShowSubscriberOnPort(DetailView):
class ShowSubscriberOnPort(global_base_views.RedirectWhenErrorMixin, DetailView):
template_name = 'devapp/manage_ports/modal_show_subscriber_on_port.html' template_name = 'devapp/manage_ports/modal_show_subscriber_on_port.html'
http_method_names = ['get'] http_method_names = ['get']
@ -190,6 +191,14 @@ class ShowSubscriberOnPort(DetailView):
obj = Abon.objects.get(device_id=dev_id, dev_port_id=port_id) obj = Abon.objects.get(device_id=dev_id, dev_port_id=port_id)
except Abon.DoesNotExist: except Abon.DoesNotExist:
raise Http404(gettext('Subscribers on port does not exist')) raise Http404(gettext('Subscribers on port does not exist'))
except Abon.MultipleObjectsReturned:
errmsg = gettext('More than one subscriber on device port')
# messages.error(self.request, errmsg)
raise global_base_views.RedirectWhenError(
resolve_url('devapp:fix_port_conflict', group_id=self.kwargs.get('group_id'), device_id=dev_id,
port_id=port_id),
errmsg
)
return obj return obj
@ -370,7 +379,7 @@ def devview(request, device_id):
}) })
except EasySNMPError: except EasySNMPError:
messages.error(request, _('SNMP error on device')) messages.error(request, _('SNMP error on device'))
except DeviceDBException as e:
except (DeviceDBException, DeviceImplementationError) as e:
messages.error(request, e) messages.error(request, e)
return render(request, 'devapp/custom_dev_page/' + template_name, { return render(request, 'devapp/custom_dev_page/' + template_name, {
'dev': dev 'dev': dev
@ -399,7 +408,7 @@ def toggle_port(request, device_id, portid, status=0):
except EasySNMPTimeoutError: except EasySNMPTimeoutError:
messages.error(request, _('wait for a reply from the SNMP Timeout')) messages.error(request, _('wait for a reply from the SNMP Timeout'))
except EasySNMPError as e: except EasySNMPError as e:
messages.error(request, e)
messages.error(request, 'EasySNMPError: %s' % e)
return redirect('devapp:view', dev.group.pk if dev.group is not None else 0, device_id) return redirect('devapp:view', dev.group.pk if dev.group is not None else 0, device_id)
@ -466,8 +475,8 @@ def fix_onu(request):
if parent is not None: if parent is not None:
manobj = parent.get_manager_object() manobj = parent.get_manager_object()
ports = manobj.get_list_keyval('.1.3.6.1.4.1.3320.101.10.1.1.3') ports = manobj.get_list_keyval('.1.3.6.1.4.1.3320.101.10.1.1.3')
text = '<span class="glyphicon glyphicon-ok"></span> <span class="hidden-xs">%s</span>' %\
(_('Device with mac address %(mac)s does not exist') % {'mac': mac})
text = '<span class="glyphicon glyphicon-ok"></span> <span class="hidden-xs">%s</span>' % \
(_('Device with mac address %(mac)s does not exist') % {'mac': mac})
for srcmac, snmpnum in ports: for srcmac, snmpnum in ports:
# 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])
@ -501,7 +510,7 @@ def fix_port_conflict(request, group_id, device_id, port_id):
}) })
class OnDevDown(AllowedSubnetMixin, HashAuthView):
class OnDevDown(global_base_views.AllowedSubnetMixin, global_base_views.HashAuthView):
# #
# Api view for monitoring devices # Api view for monitoring devices
# #

69
djing/global_base_views.py

@ -1,14 +1,27 @@
from hashlib import sha256 from hashlib import sha256
from json import dumps
from django.views.generic.base import View from django.views.generic.base import View
from django.http.response import HttpResponseForbidden
from django.http.response import HttpResponseForbidden, Http404, HttpResponseRedirect, HttpResponse
from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.views.generic import ListView
from netaddr import IPNetwork, IPAddress from netaddr import IPNetwork, IPAddress
from django.core.paginator import InvalidPage, EmptyPage
API_AUTH_SECRET = getattr(settings, 'API_AUTH_SECRET') API_AUTH_SECRET = getattr(settings, 'API_AUTH_SECRET')
API_AUTH_SUBNET = getattr(settings, 'API_AUTH_SUBNET') API_AUTH_SUBNET = getattr(settings, 'API_AUTH_SUBNET')
class RedirectWhenError(Exception):
def __init__(self, url, failed_message=None):
self.url = url
if failed_message is not None:
self.message = failed_message
def __str__(self):
return self.message or ''
class HashAuthView(View): class HashAuthView(View):
@staticmethod @staticmethod
@ -87,4 +100,54 @@ class OrderingMixin(object):
if direction == 'down': if direction == 'down':
dfx = '-' dfx = '-'
if order_by: if order_by:
return ["%s%s" % (dfx, order_by)]
return "%s%s" % (dfx, order_by)
class RedirectWhenErrorMixin(object):
def get(self, request, *args, **kwargs):
try:
return super(RedirectWhenErrorMixin, self).get(request, *args, **kwargs)
except RedirectWhenError as e:
if request.is_ajax():
return HttpResponse(dumps({
'url': e.url,
'text': e.message or ''
}))
else:
return HttpResponseRedirect(e.url)
class BaseListWithFiltering(RedirectWhenErrorMixin, ListView):
"""
When queryset contains filter and pagination than data may be missing,
and original code is raising 404 error. We want to redirect without pagination.
"""
def paginate_queryset(self, queryset, page_size):
paginator = self.get_paginator(
queryset, page_size, orphans=self.get_paginate_orphans(),
allow_empty_first_page=self.get_allow_empty())
page_kwarg = self.page_kwarg
page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1
try:
page_number = int(page)
except ValueError:
if page == 'last':
page_number = paginator.num_pages
else:
raise Http404(_("Page is not 'last', nor can it be converted to an int."))
try:
page = paginator.page(page_number)
return paginator, page, page.object_list, page.has_other_pages()
except EmptyPage:
# remove pagination from url
url = self.request.GET.copy()
del url[self.page_kwarg]
raise RedirectWhenError("%s?%s" % (self.request.path, url.urlencode()),
_('Filter does not contains data, filter without pagination'))
except InvalidPage as e:
raise Http404(_('Invalid page (%(page_number)s): %(message)s') % {
'page_number': page_number,
'message': str(e)
})

2
djing/local_settings.py.template

@ -57,3 +57,5 @@ API_AUTH_SECRET = 'your api secret'
# Allowed subnet for api # Allowed subnet for api
API_AUTH_SUBNET = '127.0.0.0/8' API_AUTH_SUBNET = '127.0.0.0/8'
# Company name
COMPANY_NAME = 'Your company name'

6
djing/settings.py

@ -86,6 +86,9 @@ TEMPLATES = [
'taskapp.context_proc.get_active_tasks_count', 'taskapp.context_proc.get_active_tasks_count',
'msg_app.context_processors.get_new_messages_count' 'msg_app.context_processors.get_new_messages_count'
], ],
'libraries': {
'globaltags': 'djing.templatetags.globaltags',
}
}, },
}, },
] ]
@ -187,3 +190,6 @@ API_AUTH_SECRET = local_settings.API_AUTH_SECRET
# Allowed subnet for api # Allowed subnet for api
API_AUTH_SUBNET = local_settings.API_AUTH_SUBNET API_AUTH_SUBNET = local_settings.API_AUTH_SUBNET
# Company name
COMPANY_NAME = local_settings.COMPANY_NAME

0
djing/templatetags/__init__.py

9
djing/templatetags/globaltags.py

@ -0,0 +1,9 @@
from django import template
from django.conf import settings
register = template.Library()
@register.simple_tag
def global_var(var_name):
return getattr(settings, var_name, '')

3
mapapp/templates/maps/dot.html

@ -1,6 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load globaltags %}
{% block main %} {% block main %}
<ol class="breadcrumb"> <ol class="breadcrumb">
@ -20,7 +21,7 @@
<div class="row"> <div class="row">
<div class="{% if dot.id %}col-sm-6{% else %}col-sm-12{% endif %}"> <div class="{% if dot.id %}col-sm-6{% else %}col-sm-12{% endif %}">
<form role="form" action="{{ form_url }}" method="post" enctype="multipart/form-data">{% csrf_token %} <form role="form" action="{{ form_url }}" method="post" enctype="multipart/form-data">{% csrf_token %}
<input type="hidden" name="MAX_FILE_SIZE" value="{{ FILE_UPLOAD_MAX_MEMORY_SIZE }}"/>
<input type="hidden" name="MAX_FILE_SIZE" value="{% global_var 'FILE_UPLOAD_MAX_MEMORY_SIZE' %}"/>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{% trans 'Map point' %}</h3> <h3 class="panel-title">{% trans 'Map point' %}</h3>

4
mapapp/templates/maps/modal_add_dot.html

@ -1,6 +1,6 @@
{% load i18n %}{% load bootstrap3 %}
{% load i18n %}{% load bootstrap3 %}{% load globaltags %}
<form action="{% url 'mapapp:modal_add_dot' %}" class="form-ajax" method="post" enctype="multipart/form-data">{% csrf_token %} <form action="{% url 'mapapp:modal_add_dot' %}" class="form-ajax" method="post" enctype="multipart/form-data">{% csrf_token %}
<input type="hidden" name="MAX_FILE_SIZE" value="{{ FILE_UPLOAD_MAX_MEMORY_SIZE }}"/>
<input type="hidden" name="MAX_FILE_SIZE" value="{% global_var 'FILE_UPLOAD_MAX_MEMORY_SIZE' %}"/>
<div class="modal-header primary"> <div class="modal-header primary">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-warning-sign"></span>{% trans 'Add point' %}</h4> <h4 class="modal-title"><span class="glyphicon glyphicon-warning-sign"></span>{% trans 'Add point' %}</h4>

359
migrate_to_0.2.py

@ -0,0 +1,359 @@
#!/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')

32
queue_mngr.py

@ -4,6 +4,8 @@ from pickle import loads
from pid.decorator import pidfile from pid.decorator import pidfile
import socket import socket
import django import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
from mydefs import LogicError
''' '''
@ -18,19 +20,23 @@ obj = {
def on_new_data(client_sock, ip): def on_new_data(client_sock, ip):
data = client_sock.recv(16384)
data = loads(data)
action = data['cmd']
if action == 'commit':
dhcp_commit(
data['client_ip'], data['client_mac'],
data['switch_mac'], data['switch_port']
)
elif action == 'expiry':
dhcp_expiry(data['client_ip'])
elif action == 'release':
dhcp_release(data['client_ip'])
client_sock.close()
try:
data = client_sock.recv(16384)
data = loads(data)
action = data['cmd']
if action == 'commit':
dhcp_commit(
data['client_ip'], data['client_mac'],
data['switch_mac'], data['switch_port']
)
elif action == 'expiry':
dhcp_expiry(data['client_ip'])
elif action == 'release':
dhcp_release(data['client_ip'])
except LogicError as e:
print('LogicError', e)
finally:
client_sock.close()
@pidfile(pidname='queue_mngr.py.pid', piddir='/run') @pidfile(pidname='queue_mngr.py.pid', piddir='/run')

BIN
static/clientside/bc.png

Before

Width: 80  |  Height: 71  |  Size: 8.7 KiB

After

Width: 80  |  Height: 80  |  Size: 4.2 KiB

4
static/clientside/custom.css

@ -5,10 +5,6 @@ img.navbar-brand {
margin: 0 15px 0 0; margin: 0 15px 0 0;
} }
body > .container {
padding: 60px 15px 0;
}
.table thead { .table thead {
background-color: #ddd; background-color: #ddd;
} }

BIN
static/img/bcgr.png

Before

Width: 57  |  Height: 50  |  Size: 6.0 KiB

After

Width: 50  |  Height: 49  |  Size: 2.0 KiB

15
static/js/my.js

@ -214,7 +214,7 @@ $(document).ajaxError(function (ev, jqXHR, ajaxSettings, thrownError) {
} }
if(settings.news_url){ if(settings.news_url){
// прверяем новости раз в минуту
// once per minute check news
var tiid = setInterval(check_news, settings.check_interval*1000); var tiid = setInterval(check_news, settings.check_interval*1000);
//Notification.requestPermission(on_ask_perm); //Notification.requestPermission(on_ask_perm);
@ -266,13 +266,20 @@ $(document).ready(function () {
}); });
$('.btn-modal').on('click', function(){ $('.btn-modal').on('click', function(){
$.get(this.href, function(r){
show_ModalMyContent(r);
$.get(this.href, function(response){
console.log(response);
try{
var r = $.parseJSON(response);
console.log(r.text);
window.location.replace(r.url);
}catch (e){
show_ModalMyContent(response);
}
}); });
return false; return false;
}); });
// кнопка посылающая комманду и возвращающая результат выполнения
// button that send command and return response of that
$('.btn-cmd').on('click', function(){ $('.btn-cmd').on('click', function(){
var cmd_param = $(this).attr('data-param'); var cmd_param = $(this).attr('data-param');
var self = $(this); var self = $(this);

2
systemd_units/djing.service

@ -3,7 +3,7 @@ Description=A job for djing
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/python3 cron.py > /dev/null
ExecStart=/usr/bin/python3 cron.py
WorkingDirectory=/srv/http/djing WorkingDirectory=/srv/http/djing
User=http User=http
Group=http Group=http

2
systemd_units/djing_queue.service

@ -6,7 +6,7 @@ Type=simple
ExecStart=/usr/bin/python3 ./queue_mngr.py > /dev/null ExecStart=/usr/bin/python3 ./queue_mngr.py > /dev/null
PIDFile=/run/queue_mngr.py.pid PIDFile=/run/queue_mngr.py.pid
WorkingDirectory=/var/www/djing WorkingDirectory=/var/www/djing
TimeoutSec=15
TimeoutSec=30
Restart=always Restart=always
User=http User=http
Group=http Group=http

2
systemd_units/djing_rotate.service

@ -4,7 +4,7 @@ Description=A job for rotate djing netflow data
[Service] [Service]
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin" Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"
Type=oneshot Type=oneshot
ExecStart=/bin/bash -c "kill -HUP `cat /run/flow.pid.6343`"
ExecStart=/bin/bash -c "kill -HUP `/bin/cat /run/flow.pid.6343`"
User=root User=root
Group=root Group=root

2
systemd_units/djing_telebot.service

@ -3,7 +3,7 @@ Description=Djing telegram bot
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/python3 ./telebot.py > /dev/null
ExecStart=/usr/bin/python3 ./telebot.py
PIDFile=/run/djing_telebot.pid PIDFile=/run/djing_telebot.pid
WorkingDirectory=/var/www/djing WorkingDirectory=/var/www/djing
TimeoutSec=9 TimeoutSec=9

23
systemd_units/webdav_backup.py

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import webdav.client as wc
from webdav.client import WebDavException
from sys import argv from sys import argv
from datetime import datetime, timedelta
import webdav.client as wc
options = { options = {
@ -10,11 +10,26 @@ options = {
'webdav_password': "YANDEX PASSWORD" 'webdav_password': "YANDEX PASSWORD"
} }
def remove_old_files(border_time : datetime, client):
# files that older than border_time will be removed
for file in client.list('ISBackups'):
fdate = datetime.strptime(file, 'djing%Y-%m-%d_%H.%M.%S.sql.gz')
if fdate < border_time:
del_fname = 'ISBackups/%' % file
client.clean( del_fname )
print("rm %s" % del_fname)
if __name__ == '__main__': if __name__ == '__main__':
reqfile = argv[1] reqfile = argv[1]
try: try:
client = wc.Client(options) client = wc.Client(options)
client.upload_sync(remote_path="ISBackups/%s" % reqfile, local_path="/var/backups/%s" % reqfile)
except WebDavException as we:
if reqfile == 'rotate':
border_time = datetime.now() - timedelta(month=3)
remove_old_files(border_time, client)
else:
client.upload_sync(remote_path="ISBackups/%s" % reqfile, local_path="/var/backups/%s" % reqfile)
except wc.WebDavException as we:
print(we, type(we)) print(we, type(we))

4
taskapp/locale/ru/LC_MESSAGES/django.po

@ -456,3 +456,7 @@ msgstr "Исполнители"
msgid "Author does not specified" msgid "Author does not specified"
msgstr "Автор не указан" msgstr "Автор не указан"
msgid "Name and comment count"
msgstr "Имя и количество комментов"

3
taskapp/templates/taskapp/add_edit_task.html

@ -1,6 +1,7 @@
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} {% extends request.is_ajax|yesno:'bajax.html,base.html' %}
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load globaltags %}
{% block main %} {% block main %}
<ol class="breadcrumb"> <ol class="breadcrumb">
@ -28,7 +29,7 @@
{% else %} {% else %}
<form role="form" action="{% url 'taskapp:add' %}" method="post" enctype="multipart/form-data"> <form role="form" action="{% url 'taskapp:add' %}" method="post" enctype="multipart/form-data">
{% endif %} {% endif %}
{% csrf_token %}<input type="hidden" name="MAX_FILE_SIZE" value="{{ FILE_UPLOAD_MAX_MEMORY_SIZE }}"/>
{% csrf_token %}<input type="hidden" name="MAX_FILE_SIZE" value="{% global_var 'FILE_UPLOAD_MAX_MEMORY_SIZE' %}"/>
{% bootstrap_icon 'tag' as ic %} {% bootstrap_icon 'tag' as ic %}
{% bootstrap_field form.descr addon_before=ic %} {% bootstrap_field form.descr addon_before=ic %}

15
taskapp/templates/taskapp/tasklist.html

@ -7,13 +7,13 @@
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th class="col-sm-3">{% trans 'Name' %}</th>
<th class="col-sm-1">{% trans 'Address' %}</th>
<th class="col-sm-1">{% trans 'The nature of the damage' %}</th>
<th class="col-sm-4">{% trans 'Description' %}</th>
<th class="col-sm-1">{% trans 'Task author' %}</th>
<th class="col-sm-1">{% trans 'Date of create' %}</th>
<th class="col-sm-1">{% trans 'Actions' %}</th>
<th class="col-sm-3 col-xs-3">{% trans 'Name and comment count' %}</th>
<th class="col-sm-1 col-xs-2">{% trans 'Address' %}</th>
<th class="col-sm-1 col-xs-1">{% trans 'The nature of the damage' %}</th>
<th class="col-sm-4 col-xs-4">{% trans 'Description' %}</th>
<th class="col-sm-1 col-xs-1">{% trans 'Task author' %}</th>
<th class="col-sm-1 hidden-xs">{% trans 'Date of create' %}</th>
<th class="col-sm-1 col-xs-1">{% trans 'Actions' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -40,6 +40,7 @@
<a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" title="{{ task.abon.description|default:'' }}" data-toggle="tooltip"> <a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}" title="{{ task.abon.description|default:'' }}" data-toggle="tooltip">
{{ task.abon.get_full_name }} {{ task.abon.get_full_name }}
</a> </a>
{% if task.comment_count > 0 %}({{ task.comment_count }}){% endif %}
</td> </td>
<td> <td>
{{ task.abon.group.title }}, {{ task.abon.street|default:'' }} {{ task.abon.house|default:'' }} {{ task.abon.group.title }}, {{ task.abon.street|default:'' }} {{ task.abon.house|default:'' }}

3
taskapp/templates/taskapp/tasklist_all.html

@ -19,7 +19,7 @@
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th class="col-sm-2">{% trans 'Name' %}</th>
<th class="col-sm-2">{% trans 'Name and comment count' %}</th>
<th class="col-sm-2">{% trans 'Address' %}</th> <th class="col-sm-2">{% trans 'Address' %}</th>
<th class="col-sm-1">{% trans 'The nature of the damage' %}</th> <th class="col-sm-1">{% trans 'The nature of the damage' %}</th>
<th class="col-sm-3">{% trans 'Description' %}</th> <th class="col-sm-3">{% trans 'Description' %}</th>
@ -53,6 +53,7 @@
<a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}"> <a href="{% url 'abonapp:abon_home' task.abon.group.pk task.abon.pk %}">
{{ task.abon.get_full_name }} {{ task.abon.get_full_name }}
</a> </a>
{% if task.comment_count > 0 %}({{ task.comment_count }}){% endif %}
</td> </td>
<td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td> <td>{{ task.abon.group.title }}, {{ task.abon.street|default:_('Not assigned') }} {{ task.abon.house|default:_('Not assigned') }}</td>

10
taskapp/templates/taskapp/tasklist_failed.html

@ -46,8 +46,14 @@
<td>{{ task.get_mode_display }}</td> <td>{{ task.get_mode_display }}</td>
<td>{{ task.descr }}</td> <td>{{ task.descr }}</td>
<td><a href="{% url 'acc_app:other_profile' task.author.pk %}" data-toggle="tooltip"
title="{{ task.author.get_full_name }}">{{ task.author.username }}</a></td>
<td>
{% if task.author %}
<a href="{% url 'acc_app:other_profile' task.author.pk %}" data-toggle="tooltip"
title="{{ task.author.get_full_name }}">{{ task.author.username }}</a>
{% else %}
{% trans 'Not assigned' %}
{% endif %}
</td>
<td class="hidden-xs">{{ task.time_of_create|date:'d E H:i' }}</td> <td class="hidden-xs">{{ task.time_of_create|date:'d E H:i' }}</td>
<td class="btn-group btn-group-sm btn-group-justified"> <td class="btn-group btn-group-sm btn-group-justified">

2
taskapp/templates/taskapp/tasklist_own.html

@ -52,7 +52,7 @@
{% endif %} {% endif %}
<td>{{ task.get_mode_display }}</td> <td>{{ task.get_mode_display }}</td>
<td>{{ task.descr }}</td>
<td>{{ task.descr|default:'' }}</td>
<td>{{ task.get_state_display }}</td> <td>{{ task.get_state_display }}</td>
<td class="hidden-xs">{{ task.time_of_create|date:'d E H:i' }}</td> <td class="hidden-xs">{{ task.time_of_create|date:'d E H:i' }}</td>

5
taskapp/views.py

@ -3,6 +3,7 @@ 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
from django.db.models import Count
from django.shortcuts import redirect, get_object_or_404, resolve_url from django.shortcuts import redirect, get_object_or_404, resolve_url
from django.contrib import messages from django.contrib import messages
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -37,6 +38,7 @@ class NewTasksView(BaseTaskListView):
def get_queryset(self): def get_queryset(self):
return Task.objects.filter(recipients=self.request.user, state='S') \ return Task.objects.filter(recipients=self.request.user, state='S') \
.annotate(comment_count=Count('extracomment')) \
.select_related('abon', 'abon__street', 'abon__group', 'author') .select_related('abon', 'abon__street', 'abon__group', 'author')
@ -85,7 +87,8 @@ class AllTasksListView(BaseTaskListView):
context_object_name = 'tasks' context_object_name = 'tasks'
def get_queryset(self): def get_queryset(self):
return Task.objects.select_related('abon', 'abon__street', 'abon__group', 'author')
return Task.objects.annotate(comment_count=Count('extracomment')) \
.select_related('abon', 'abon__street', 'abon__group', 'author')
@login_required @login_required

6
templates/all_base.html

@ -1,7 +1,7 @@
<!DOCTYPE html>
<!DOCTYPE html>{% load globaltags %}
<html lang="ru_RU"> <html lang="ru_RU">
<head> <head>
<title>InternetService - Админка</title>
<title>{% global_var 'COMPANY_NAME' %} - Админка</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<!--[if lt IE 9]><meta http-equiv="refresh" content="0;URL=/static/bad_ie.html" /><![endif]--> <!--[if lt IE 9]><meta http-equiv="refresh" content="0;URL=/static/bad_ie.html" /><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -37,7 +37,7 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand hidden-xs" href="{% url 'acc_app:other_profile' request.user.id %}">InternetService</a>
<a class="navbar-brand hidden-xs" href="{% url 'acc_app:other_profile' request.user.id %}">{% global_var 'COMPANY_NAME' %}</a>
</div> </div>
<div class="navbar-collapse collapse"> <div class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">

Loading…
Cancel
Save