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>
</thead>
<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 %}
{% if human.is_active %}
<tr>
@ -94,11 +94,11 @@
<td>{{ human.house|default:'-' }}</td>
<td><a href="tel:{{ human.telephone }}">{{ human.telephone }}</a></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 %}
{{ human.active_tariff.tariff.title }}
{{ human.current_tariff.tariff.title }}
{% endif %}
{% else %}&mdash;&mdash;&mdash;
{% endif %}
@ -109,7 +109,7 @@
{% endfor %}
</td>
<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">
<span class="glyphicon glyphicon-remove"></span>
</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.shortcuts import render, redirect, get_object_or_404, resolve_url
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.utils.translation import gettext_lazy as _
from django.utils.decorators import method_decorator
@ -27,13 +27,13 @@ from statistics.models import getModel
from group_app.models import Group
from guardian.shortcuts import get_objects_for_user, assign_perm
from guardian.decorators import permission_required_or_403 as permission_required
from djing.global_base_views import OrderingMixin
from djing.global_base_views import OrderingMixin, BaseListWithFiltering
PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
@method_decorator([login_required, mydefs.only_admins], name='dispatch')
class BaseAbonListView(ListView, OrderingMixin):
class BaseAbonListView(OrderingMixin, BaseListWithFiltering):
paginate_by = PAGINATION_ITEMS_PER_PAGE
http_method_names = ['get']
@ -45,7 +45,7 @@ class PeoplesListView(BaseAbonListView):
def get_queryset(self):
street_id = mydefs.safe_int(self.request.GET.get('street'))
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:
peoples_list = peoples_list.filter(group__pk=gid, street=street_id)
else:
@ -60,7 +60,11 @@ class PeoplesListView(BaseAbonListView):
pass
except mydefs.LogicError as 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
def get_context_data(self, **kwargs):
@ -545,7 +549,8 @@ def clear_dev(request, gid, uid):
try:
abon = models.Abon.objects.get(pk=uid)
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'))
except models.Abon.DoesNotExist:
messages.error(request, _('Abon does not exist'))

2
accounts_app/views.py

@ -213,7 +213,7 @@ def perms(request, uid):
klasses = (
'abonapp.Abon', 'accounts_app.UserProfile',
'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', {
'userprofile': userprofile,

5
agent/netflow/netflow_handler.py

@ -4,6 +4,7 @@ import sys
import os
from importlib import import_module
if __name__ == '__main__':
if len(sys.argv) < 2:
print("File name of netflow required")
@ -48,8 +49,8 @@ if __name__ == '__main__':
db.close()
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"' % {
'CUR_DIR': cur_dir,
'TMP_IPUSER_FILE': tmp_ipuser_file,

2
chatbot/models.py

@ -57,7 +57,7 @@ class MessageQueue(models.Model):
('r', 'Read')
)
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')
objects = MessageQueueManager()

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

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n"
@ -54,6 +54,91 @@ msgstr "Подтвердить"
msgid "Cancel"
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
msgid "Pick service"
msgstr "Заказать услугу"
@ -62,20 +147,32 @@ msgstr "Заказать услугу"
msgid "Are you sure you want to order the service?"
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
#, python-format
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 ""
#: 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"
msgstr "Заказать"
#: templates/clientsideapp/modal_service_buy.html:24
#: templates/clientsideapp/modal_service_buy.html:25
msgid "Close"
msgstr "Закрыть"
@ -99,9 +196,37 @@ msgstr "Комментарий"
msgid "You have not spent payments"
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
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
#, 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' %}
{% load i18n %}
{% block client_main %}
<div class="page-header">
<h3>Ваши долги</h3>
<h3>{% trans 'Your debt' %}</h3>
</div>
<table class="table table-striped table-bordered">
<thead>
<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>
</thead>
<tbody>
{% for debt in debts %}
<tr>
<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.date_create|date:'d b H:i' }}</td>
<td>
@ -27,21 +28,21 @@
{{ debt.date_pay|date:'d b H:i' }}
{% else %}
{% if debt.status %}
Создан оплаченным
{% trans 'Created paid' %}
{% else %}
Ещё не оплачен
{% trans 'Not yet paid' %}
{% endif %}
{% endif %}
</td>
<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>
</a>
</td>
</tr>
{% empty %}
<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>
{% endfor %}
</tbody>

51
clientsideapp/templates/clientsideapp/ext.html

@ -1,10 +1,10 @@
<!DOCTYPE HTML>
<!DOCTYPE HTML>{% load globaltags %}{% load i18n %}
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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/clientside/custom.css" rel="stylesheet">
<script src="/static/js/jquery-2.2.4.min.js"></script>
@ -19,6 +19,8 @@
<div class="modal-content" id="modContent"></div>
</div>
</div>
{% url 'client_side:home' as client_side_home %}
<!-- Fixed navbar -->
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
@ -30,48 +32,36 @@
<span class="icon-bar"></span>
</button>
<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 class="collapse navbar-collapse">
<ul class="nav navbar-nav">
{% url 'client_side:home' as client_side_home %}
<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>
{% url 'client_side:pays' as client_side_pays %}
<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>
{% url 'client_side:services' as client_side_services %}
<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><a href="#contact">График трафика</a></li>-->
<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">
<!--<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>
</li>
</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>
</div>
@ -80,11 +70,12 @@
<div class="container">
{% 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 %}
{% include 'message_block.html' %}
@ -95,7 +86,7 @@
<div id="footer">
<div class="container">
<p class="text-muted">
Напишите разработчику <i>is-ttk@ya.ru</i>.
{% include 'clientsideapp/custom_pages/footer.htm' %}
</p>
</div>
</div>

71
clientsideapp/templates/clientsideapp/index.html

@ -1,73 +1,4 @@
{% extends 'clientsideapp/ext.html' %}
{% 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 %}

6
clientsideapp/templates/clientsideapp/modal_service_buy.html

@ -6,7 +6,7 @@
</div>
<div class="modal-body">
<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>
@ -15,8 +15,8 @@
<!--<p>{# {% blocktrans trimmed with speedIn=service.speedIn speedOut=service.speedOut amount=service.amount %}
Inbound speed: {{ speedIn }} 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 class="modal-footer">
<button type="submit" class="btn btn-sm btn-primary">

44
clientsideapp/templates/clientsideapp/services.html

@ -8,38 +8,42 @@
<div class="row">
<div class="col-lg-7">
{% if current_service %}
<div class="panel panel-default">
<div class="panel-heading">
Ваша текущая услуга
{% trans 'Your current service' %}
</div>
<div class="panel-body">
<h3 class="panel-title">{{ current_service.tariff.title }}</h3><br>
<dl class="dl-horizontal">
<dt>Дата подключения</dt>
<dt>{% trans 'The date of connection' %}</dt>
<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>
<dt>Стоимость</dt>
<dt>{% trans 'Cost' %}</dt>
<dd>{{ current_service.tariff.amount }} {% trans 'currency' %}</dd>
</dl>
<p>{{ current_service.tariff.descr }}</p>
</div>
</div>
{% else %}
<div class="alert alert-warning" role="alert">
<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>
{% endif %}
</div>
<div class="col-lg-5">
<div class="panel panel-default">
<div class="panel-heading">
Доступные для заказа услуги
{% trans 'Services available for ordering' %}
</div>
<div class="panel-body">
<div class="container-fluid">
@ -48,7 +52,7 @@
<div class="col-lg-12">
<h3>{{ tarif.title }}</h3>
<i>{{ tarif.amount }} руб.</i>
<i>{{ tarif.amount }} {% trans 'currency' %}</i>
<p>{{ tarif.descr }}</p>
{% if request.user.is_staff or current_service %}
@ -61,7 +65,7 @@
</div>
{% empty %}
<div class="col-lg-4">
<h3 class="panel-title">Нет доступных услуг для заказа</h3>
<h3 class="panel-title">{% trans 'No services available for ordering' %}</h3>
</div>
{% endfor %}
</div>
@ -70,28 +74,10 @@
</div>
</div>
</div>
{% include 'clientsideapp/services_ext.txt' %}
{% include 'clientsideapp/custom_pages/service.htm' with active_service=current_service %}
</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 %}

21
devapp/base_intr.py

@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
from easysnmp import Session
class DeviceImplementationError(Exception):
pass
class DevBase(object, metaclass=ABCMeta):
def __init__(self, dev_instance=None):
@ -10,19 +13,19 @@ class DevBase(object, metaclass=ABCMeta):
@staticmethod
def description():
"""Возвращает текстовое описание"""
pass
@abstractmethod
def reboot(self):
"""Перезагружает устройство"""
pass
@abstractmethod
def get_ports(self):
"""Получаем инфу о портах"""
pass
@abstractmethod
def get_device_name(self):
"""Получаем имя устройства по snmp"""
"""Return device name by snmp"""
@abstractmethod
def uptime(self):
@ -30,17 +33,17 @@ class DevBase(object, metaclass=ABCMeta):
@abstractmethod
def get_template_name(self):
"""Получаем путь к html шаблону отображения устройства"""
"""Return path to html template for device"""
@staticmethod
@abstractmethod
def has_attachable_to_subscriber():
"""Можно-ли подключать устройство к абоненту"""
"""Can connect device to subscriber"""
@staticmethod
@abstractmethod
def is_use_device_port():
"""True если при авторизации по opt82 используется порт"""
"""True if used device port while opt82 authorization"""
class BasePort(object, metaclass=ABCMeta):
@ -70,7 +73,7 @@ class SNMPBaseWorker(object, metaclass=ABCMeta):
self.ses = Session(hostname=ip, community=community, version=ver)
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):
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 datetime import timedelta
from easysnmp import EasySNMPTimeoutError
from .base_intr import DevBase, SNMPBaseWorker, BasePort
from .base_intr import DevBase, SNMPBaseWorker, BasePort, DeviceImplementationError
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')
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'))
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'))
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 = []
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):
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_plural = _('Devices')
ordering = ['comment']
ordering = ['id']
def get_abons(self):
pass

2
devapp/onu_register.sh

@ -29,6 +29,6 @@ if grep "${MAC}" "${DHCP_PATH}/${PART_CODE}.conf" > /dev/null; then
else
# add new mac
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

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

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

27
devapp/views.py

@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _, gettext
from easysnmp import EasySNMPTimeoutError, EasySNMPError
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 abonapp.models import Abon
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.models import ChatException
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 .forms import DeviceForm, PortForm
from mydefs import safe_int
@ -35,7 +36,7 @@ class BaseDeviceListView(ListView):
@method_decorator([login_required, only_admins], name='dispatch')
class DevicesListView(BaseDeviceListView, OrderingMixin):
class DevicesListView(BaseDeviceListView, global_base_views.OrderingMixin):
context_object_name = 'devices'
template_name = 'devapp/devices.html'
@ -62,7 +63,7 @@ class DevicesListView(BaseDeviceListView, OrderingMixin):
@method_decorator([login_required, only_admins], name='dispatch')
class DevicesWithoutGroupsListView(BaseDeviceListView, OrderingMixin):
class DevicesWithoutGroupsListView(BaseDeviceListView, global_base_views.OrderingMixin):
context_object_name = 'devices'
template_name = 'devapp/devices_null_group.html'
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')
class ShowSubscriberOnPort(DetailView):
class ShowSubscriberOnPort(global_base_views.RedirectWhenErrorMixin, DetailView):
template_name = 'devapp/manage_ports/modal_show_subscriber_on_port.html'
http_method_names = ['get']
@ -190,6 +191,14 @@ class ShowSubscriberOnPort(DetailView):
obj = Abon.objects.get(device_id=dev_id, dev_port_id=port_id)
except Abon.DoesNotExist:
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
@ -370,7 +379,7 @@ def devview(request, device_id):
})
except EasySNMPError:
messages.error(request, _('SNMP error on device'))
except DeviceDBException as e:
except (DeviceDBException, DeviceImplementationError) as e:
messages.error(request, e)
return render(request, 'devapp/custom_dev_page/' + template_name, {
'dev': dev
@ -399,7 +408,7 @@ def toggle_port(request, device_id, portid, status=0):
except EasySNMPTimeoutError:
messages.error(request, _('wait for a reply from the SNMP Timeout'))
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)
@ -466,8 +475,8 @@ def fix_onu(request):
if parent is not None:
manobj = parent.get_manager_object()
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:
# convert bytes mac address to str presentation mac address
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
#

69
djing/global_base_views.py

@ -1,14 +1,27 @@
from hashlib import sha256
from json import dumps
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.views.generic import ListView
from netaddr import IPNetwork, IPAddress
from django.core.paginator import InvalidPage, EmptyPage
API_AUTH_SECRET = getattr(settings, 'API_AUTH_SECRET')
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):
@staticmethod
@ -87,4 +100,54 @@ class OrderingMixin(object):
if direction == 'down':
dfx = '-'
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
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',
'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
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' %}
{% load i18n %}
{% load bootstrap3 %}
{% load globaltags %}
{% block main %}
<ol class="breadcrumb">
@ -20,7 +21,7 @@
<div class="row">
<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 %}
<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-heading">
<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 %}
<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">
<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>

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
import socket
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):
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')

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;
}
body > .container {
padding: 60px 15px 0;
}
.table thead {
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){
// прверяем новости раз в минуту
// once per minute check news
var tiid = setInterval(check_news, settings.check_interval*1000);
//Notification.requestPermission(on_ask_perm);
@ -266,13 +266,20 @@ $(document).ready(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;
});
// кнопка посылающая комманду и возвращающая результат выполнения
// button that send command and return response of that
$('.btn-cmd').on('click', function(){
var cmd_param = $(this).attr('data-param');
var self = $(this);

2
systemd_units/djing.service

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

2
systemd_units/djing_queue.service

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

2
systemd_units/djing_rotate.service

@ -4,7 +4,7 @@ Description=A job for rotate djing netflow data
[Service]
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"
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
Group=root

2
systemd_units/djing_telebot.service

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

23
systemd_units/webdav_backup.py

@ -1,7 +1,7 @@
#!/usr/bin/env python3
import webdav.client as wc
from webdav.client import WebDavException
from sys import argv
from datetime import datetime, timedelta
import webdav.client as wc
options = {
@ -10,11 +10,26 @@ options = {
'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__':
reqfile = argv[1]
try:
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))

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

@ -456,3 +456,7 @@ msgstr "Исполнители"
msgid "Author does not specified"
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' %}
{% load i18n %}
{% load bootstrap3 %}
{% load globaltags %}
{% block main %}
<ol class="breadcrumb">
@ -28,7 +29,7 @@
{% else %}
<form role="form" action="{% url 'taskapp:add' %}" method="post" enctype="multipart/form-data">
{% 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_field form.descr addon_before=ic %}

15
taskapp/templates/taskapp/tasklist.html

@ -7,13 +7,13 @@
<table class="table table-striped table-bordered">
<thead>
<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>
</thead>
<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">
{{ task.abon.get_full_name }}
</a>
{% if task.comment_count > 0 %}({{ task.comment_count }}){% endif %}
</td>
<td>
{{ 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">
<thead>
<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-1">{% trans 'The nature of the damage' %}</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 %}">
{{ task.abon.get_full_name }}
</a>
{% if task.comment_count > 0 %}({{ task.comment_count }}){% endif %}
</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.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="btn-group btn-group-sm btn-group-justified">

2
taskapp/templates/taskapp/tasklist_own.html

@ -52,7 +52,7 @@
{% endif %}
<td>{{ task.get_mode_display }}</td>
<td>{{ task.descr }}</td>
<td>{{ task.descr|default:'' }}</td>
<td>{{ task.get_state_display }}</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.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.db.models import Count
from django.shortcuts import redirect, get_object_or_404, resolve_url
from django.contrib import messages
from django.utils.decorators import method_decorator
@ -37,6 +38,7 @@ class NewTasksView(BaseTaskListView):
def get_queryset(self):
return Task.objects.filter(recipients=self.request.user, state='S') \
.annotate(comment_count=Count('extracomment')) \
.select_related('abon', 'abon__street', 'abon__group', 'author')
@ -85,7 +87,8 @@ class AllTasksListView(BaseTaskListView):
context_object_name = 'tasks'
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

6
templates/all_base.html

@ -1,7 +1,7 @@
<!DOCTYPE html>
<!DOCTYPE html>{% load globaltags %}
<html lang="ru_RU">
<head>
<title>InternetService - Админка</title>
<title>{% global_var 'COMPANY_NAME' %} - Админка</title>
<meta charset="UTF-8">
<!--[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">
@ -37,7 +37,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</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 class="navbar-collapse collapse">
<ul class="nav navbar-nav">

Loading…
Cancel
Save