From 31d2f2e839d6c3279ac9aca033284657f054763d Mon Sep 17 00:00:00 2001 From: bashmak Date: Sat, 14 Jul 2018 16:01:28 +0300 Subject: [PATCH 01/16] Make vCards exporter --- abonapp/locale/ru/LC_MESSAGES/django.po | 3 ++ abonapp/templates/abonapp/group_list.html | 4 +++ abonapp/urls.py | 2 ++ abonapp/views.py | 44 ++++++++++++++++++++++- 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/abonapp/locale/ru/LC_MESSAGES/django.po b/abonapp/locale/ru/LC_MESSAGES/django.po index 8db7255..ea1578e 100644 --- a/abonapp/locale/ru/LC_MESSAGES/django.po +++ b/abonapp/locale/ru/LC_MESSAGES/django.po @@ -1157,3 +1157,6 @@ msgstr "История задач" msgid "Charts" msgstr "Графики" + +msgid "Export vCards" +msgstr "Экспорт в vCards" diff --git a/abonapp/templates/abonapp/group_list.html b/abonapp/templates/abonapp/group_list.html index 082329d..a433034 100644 --- a/abonapp/templates/abonapp/group_list.html +++ b/abonapp/templates/abonapp/group_list.html @@ -66,6 +66,10 @@ {% endif %} + + + + diff --git a/abonapp/urls.py b/abonapp/urls.py index 025b136..1678087 100644 --- a/abonapp/urls.py +++ b/abonapp/urls.py @@ -62,6 +62,8 @@ urlpatterns = [ url(r'^ping$', views.abon_ping, name='ping'), + url(r'contacts/vcards/$', views.vcards, name='vcards'), + # Api's url(r'^api/abons$', views.abons), url(r'^api/abon_filter$', views.search_abon), diff --git a/abonapp/views.py b/abonapp/views.py index 2c4e9f7..f4eb315 100644 --- a/abonapp/views.py +++ b/abonapp/views.py @@ -773,7 +773,49 @@ def abon_ping(request): } -@method_decorator((login_required, lib.decorators.only_admins,), name='dispatch') +@login_required +def vcards(r): + abons = models.Abon.objects.exclude(group=None).select_related('group', 'street').only( + 'username', 'fio', 'group__title', 'telephone', + 'street__name', 'house' + ) + additional_tels = models.AdditionalTelephone.objects.select_related('abon', 'abon__group', 'abon__street') + response = HttpResponse(content_type='text/x-vcard') + response['Content-Disposition'] = 'attachment; filename="contacts.vcard"' + tmpl = ("BEGIN:VCARD\r\n" + "VERSION:4.0\r\n" + "FN:%(uname)s. %(group_name)s, %(street)s %(house)s\r\n" + "IMPP:sip:%(abon_telephone)s@dial.lo\r\n" + "END:VCARD\r\n") + + def _make_vcard(): + for ab in abons.iterator(): + tel = ab.telephone + if tel: + yield tmpl % { + 'uname': ab.get_full_name(), + 'group_name': ab.group.title, + 'street': ab.street.name if ab.street else '', + 'house': ab.house, + 'abon_telephone': tel + } + if not additional_tels.exists(): + return + for add_tel in additional_tels.iterator(): + abon = add_tel.abon + yield tmpl % { + 'uname': "%s (%s)" % (add_tel.owner_name, abon.get_full_name()), + 'group_name': abon.group.title, + 'abon_telephone': add_tel.telephone, + 'street': abon.street.name if abon.street else '', + 'house': abon.house + } + + response.content = _make_vcard() + return response + + +@method_decorator((login_required, lib.decorators.only_admins), name='dispatch') class DialsListView(BaseAbonListView): context_object_name = 'logs' template_name = 'abonapp/dial_log.html' From 1b1295280956e438671be55bafc83a3093e5bc83 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Sun, 15 Jul 2018 01:10:39 +0000 Subject: [PATCH 02/16] Somthing make docs about extra functional, user page and dhcp --- README.md | 5 ++++- docs/dhcp.md | 28 ++++++++++++++++++++++++++++ docs/extra_func.md | 20 ++++++++++++++++++++ docs/map.md | 2 +- docs/netflow.md | 3 ++- docs/user_page.md | 14 ++++++++++++++ docs/views.md | 6 +++--- 7 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 docs/dhcp.md create mode 100644 docs/extra_func.md create mode 100644 docs/user_page.md diff --git a/README.md b/README.md index 827b8e1..edd7b0c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,10 @@ P.S. Возможно понадобится **Python 3.5** и выше из-з ## Содержание * [Установка](./docs/install.md) * [Сервисы и API](./docs/services.md) -* [Разработка расширений](./docs/dev.md) +* [Менеджеры устройств](./docs/dev.md) * [Сбор информации трафика по netflow](./docs/netflow.md) * [Работа с представлениями](./docs/views.md) * [Карта](./docs/map.md) +* [DHCP](./docs/dhcp.md) +* [Страница абонента](./docs/user_page.md) +* [Дополнительный функционал](./docs/extra_func.md) \ No newline at end of file diff --git a/docs/dhcp.md b/docs/dhcp.md new file mode 100644 index 0000000..b9ca933 --- /dev/null +++ b/docs/dhcp.md @@ -0,0 +1,28 @@ +## ISC-DHCP Сервер, взаимодействие с биллингом. +Вобщих чертах взаимодействие происходит с помощью скрипта **dhcp_lever.py** +в корне проекта. Запущенный DHCP сервер, при возникновении событий запускает +этот сценарий , а тот говорит биллингу подробнее что произошло. + +При событии *expiry* или *release* биллингу нужно освободить ip, а при *commit* +нужно назначить динамическую аренду ip для учётной записи абонента в биллинге. + +Сам скрипт не выполняет все эти действия, он просто отправляет полученные от dhcp +сервера параметры на url адрес для обработки dhcp. View распологается в **abonapp.views.DhcpLever**. + +### Выделение аренды ip +Как происходит выделение аренды ip, от события в dhcp сервере и до появления интернета у +абонента. + +Когда в dhcp сервере происходит событие *commit* то из **abonapp.views.DhcpLever** вызывается +функция **agent.commands.dhcp.dhcp_commit**, с помощью DHCP OPTION.82 получаем mac адрес управляемого +свича и порт через который пришёл запрос. Каждое такое устройство должно быть зарегистрировано в биллинге. +Далее ищем в базе абонента, или абонентов к которому привязано устройство с переданным mac адресом. +Проверяем может-ли данный тип устройства содержать несколько подключённых абонентов(напрмер PON ONU, в основном, +содержит одного абонента). Проверка происходит по свойству **is_use_device_port** из менеджера устройства, +которое открыто для кастомизации, подробнее в [Менеджер устройства](./docs/dev.md). +А далее, если может быть несколько абонентов, то фильтруем вывод ещё по порту свича. +Получется что на управляемом свиче мы авторизуем абонентов при помощи dhcp option.82 по маку свича и порту абонента. +Если наше устройство PON ONU(ONT) то авторизуем только по mac адресу оптического юнита(onu). + +После добавления абоненту аренды динамического ip, он(абонент) синхронизуется с nas сервером и открывается доступ +к интернету в соответствии с тарифом абонента. diff --git a/docs/extra_func.md b/docs/extra_func.md new file mode 100644 index 0000000..fb253b7 --- /dev/null +++ b/docs/extra_func.md @@ -0,0 +1,20 @@ +## Дополнительный фунционал +В процессе реализации проекта понадобился функционал, который отсутствует в базовой поставке **Django**. +Его совсем не много, но без внимания оставить нельзя. + +Все вспомогательные модули можно найти в пакете **djing.lib**. +### tln +Это модуль работы по *telnet* + +### messaging +Этот модуль помогает работать с форматами СМС сообщений. + + +### init +Содержит всякие мелкие примочки, код прост и с комментариями, зайдите посмотрите. + +## auth_decorators +Бэкенд авторизации + +## decorators +Дополнительные декораторы. diff --git a/docs/map.md b/docs/map.md index 338e9f1..f539984 100644 --- a/docs/map.md +++ b/docs/map.md @@ -24,4 +24,4 @@ ### Другое Статусы устройств на точках с одним устройством обновляются автоматически 2 раза в минуту. Это означает что когда вы изучаете карту и какое-либо устройство пропало из сети то вы -увидите это сразу без перезагрузки карты. +увидите это в течение 2х минут без перезагрузки карты. diff --git a/docs/netflow.md b/docs/netflow.md index e79bd1a..df3d33b 100644 --- a/docs/netflow.md +++ b/docs/netflow.md @@ -27,5 +27,6 @@ rm -rf djing_flow_git вы найдёте этот самый файл дампа трафика. И тут уже можно посмотреть как работает утилита **djing_flow**: > \$ ./djing_flow < /tmp/djing_flow_dump.tmp -На выходе вы получите запрос для mysql. Можно перенаправить его по конвееру в mysql, реализацию вы можете увидеть в файле +На выходе вы получите запрос для mysql. Можно перенаправить его по конвееру в mysql, рабочий пример +перенаправления этогй утилиты вы можете увидеть в файле *agent/netflow/netflow_handler.sh*. diff --git a/docs/user_page.md b/docs/user_page.md new file mode 100644 index 0000000..aa5852d --- /dev/null +++ b/docs/user_page.md @@ -0,0 +1,14 @@ +## Особенности страницы абонента. +Находится она в разделе **Абоненты** внутри группы. На этой странице вы увидите несколько логических блоков, +из которых самые важные, пожалуй, 2. Первый это **Изменение абонента** а второй **Выберите устройство**. +На первом блоке можно редактировать базовую информацию абонента. Если снять галку *Активен* то абонент перестанет +получать услуги даже при подключённой услуге. + +Блок с устройством содержит то самое устройство, к которому подключён абонент. Если это устройство не будет назначено +то биллинг не сможет авторизовать абонента по dhcp option.82. Галочка **Динамические настройки по dhcp** означает что +учётная запись абонента сможет получать динамический ip. Это означает что если галка не будет выставлена, то сколько бы +запросов не приходило с этого устройства абонент не изменить свой ip, это полезно когда абонент работает со статическим +ip. + +Вверху есть вкладки. с соответствующим названию функционалом. Например на вкладке **Тарифы** вы можете назначить +абоненту услугу или добавить периодический платёж, который абонент увидит в своём личном кабинете. diff --git a/docs/views.md b/docs/views.md index 2e190ac..91b1d55 100644 --- a/docs/views.md +++ b/docs/views.md @@ -22,8 +22,8 @@ class PaysListView(ListView): ``` -Тогда в шаблоне с bootstrap вы можете увидеть примерно такую пагинацию которую -вы конечно же можете изменить на свою. +Тогда в шаблоне с bootstrap вы можете подключить шаблон пагинации *templates/pagination.html* и +увидеть примерно такую пагинацию которую вы конечно же можете изменить на свою. ![paginator](./img/pagination.png). @@ -53,4 +53,4 @@ class PaysListView(ListView, OrderingMixin): pass ``` -Примесь *OrderingMixin* добавляет в контекст переменные *order_by* и *dir* для использования в шалоне. +Примесь *OrderingMixin* добавляет в контекст переменные *order_by* и *dir* для использования в шаблоне. From 716604e699f19870b8779825546041fe898d8c23 Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Mon, 23 Jul 2018 15:58:47 +0300 Subject: [PATCH 03/16] make email notifications --- accounts_app/models.py | 2 +- chatbot/email_bot.py | 20 ++++++++++++++++++++ chatbot/send_func.py | 5 +++++ devapp/views.py | 2 +- docs/dev.md | 2 +- msg_app/models.py | 2 +- taskapp/handle.py | 2 +- taskapp/models.py | 19 +++++++++---------- 8 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 chatbot/email_bot.py create mode 100644 chatbot/send_func.py diff --git a/accounts_app/models.py b/accounts_app/models.py index e99199c..014a948 100644 --- a/accounts_app/models.py +++ b/accounts_app/models.py @@ -95,7 +95,7 @@ class UserProfileManager(MyUserManager): class UserProfile(BaseAccount): avatar = models.ImageField(_('Avatar'), upload_to=os.path.join('user', 'avatar'), null=True, default=None) - email = models.EmailField(default='admin@example.ru') + email = models.EmailField(default='') responsibility_groups = models.ManyToManyField(Group, blank=True, verbose_name=_('Responsibility groups')) objects = UserProfileManager() diff --git a/chatbot/email_bot.py b/chatbot/email_bot.py new file mode 100644 index 0000000..889bbab --- /dev/null +++ b/chatbot/email_bot.py @@ -0,0 +1,20 @@ +from smtplib import SMTPException +from django.core.mail import send_mail +from django.conf import settings + +from chatbot.models import ChatException, MessageQueue + + +def send_notify(msg_text, account, tag='none'): + try: + MessageQueue.objects.push(msg=msg_text, user=account, tag=tag) + target_email = account.email + print('send', target_email, type(target_email), 'from', getattr(settings, 'DEFAULT_FROM_EMAIL')) + send_mail( + subject=getattr(settings, 'COMPANY_NAME', 'Djing notify'), + message=msg_text, + from_email=getattr(settings, 'DEFAULT_FROM_EMAIL'), + recipient_list=(target_email,) + ) + except SMTPException as e: + raise ChatException('SMTPException: %s' % e) diff --git a/chatbot/send_func.py b/chatbot/send_func.py new file mode 100644 index 0000000..5571f23 --- /dev/null +++ b/chatbot/send_func.py @@ -0,0 +1,5 @@ +# send via email +from .email_bot import send_notify + +# for Telegram +# from chatbot.telebot import send_notify diff --git a/devapp/views.py b/devapp/views.py index 985fcac..a4917d6 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -22,7 +22,7 @@ from accounts_app.models import UserProfile from django.conf import settings from guardian.decorators import permission_required_or_403 as permission_required from guardian.shortcuts import get_objects_for_user -from chatbot.telebot import send_notify +from chatbot.send_func import send_notify from chatbot.models import ChatException from jsonview.decorators import json_view from djing import global_base_views, MAC_ADDR_REGEX, ping, get_object_or_None diff --git a/docs/dev.md b/docs/dev.md index 94713bc..2fdb545 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -287,7 +287,7 @@ def add_tariff_range(self, tariff_list): Для того чтоб оправить важное сообщение работнику через все возможные настроенные системы(смс, телеграм, браузер) мы можем воспользоваться одной процедурой из модуля **chatbot**. ```python -from chatbot.telebot import send_notify +from chatbot.send_func import send_notify send_notify(msg_text='Text message',account=employee_profile, tag='apptag') ``` diff --git a/msg_app/models.py b/msg_app/models.py index a0becae..0014c6b 100644 --- a/msg_app/models.py +++ b/msg_app/models.py @@ -1,7 +1,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from accounts_app.models import UserProfile -from chatbot.telebot import send_notify +from chatbot.send_func import send_notify from chatbot.models import ChatException diff --git a/taskapp/handle.py b/taskapp/handle.py index cabcdfc..20fc982 100644 --- a/taskapp/handle.py +++ b/taskapp/handle.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from django.utils.translation import gettext as _ -from chatbot.telebot import send_notify +from chatbot.send_func import send_notify from chatbot.models import ChatException from djing.lib import MultipleException diff --git a/taskapp/models.py b/taskapp/models.py index e27e143..a9d3cdf 100644 --- a/taskapp/models.py +++ b/taskapp/models.py @@ -6,7 +6,7 @@ from django.conf import settings from django.utils import timezone from django.utils.translation import ugettext as _ from abonapp.models import Abon -# from .handle import handle as task_handle +from .handle import handle as task_handle TASK_PRIORITIES = ( ('A', _('Higher')), @@ -100,15 +100,14 @@ class Task(models.Model): self.save(update_fields=('state',)) def send_notification(self): - pass - #if self.abon: - # group = self.abon.group - #else: - # group = '' - #task_handle( - # self, self.author, - # self.recipients.all(), group - #) + if self.abon: + group = self.abon.group + else: + group = '' + task_handle( + self, self.author, + self.recipients.all(), group + ) def get_attachment_fname(self): return os.path.basename(self.attachment.name) From 7012cf5509789c80fb9f1fac5e6ff69adf79c0aa Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Mon, 23 Jul 2018 17:34:37 +0300 Subject: [PATCH 04/16] make send email without system libs --- chatbot/email_bot.py | 1 - djing/local_settings.py.template | 7 +++++++ djing/settings.py | 10 ++++++++++ group_app/views.py | 1 - 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/chatbot/email_bot.py b/chatbot/email_bot.py index 889bbab..6c83e21 100644 --- a/chatbot/email_bot.py +++ b/chatbot/email_bot.py @@ -9,7 +9,6 @@ def send_notify(msg_text, account, tag='none'): try: MessageQueue.objects.push(msg=msg_text, user=account, tag=tag) target_email = account.email - print('send', target_email, type(target_email), 'from', getattr(settings, 'DEFAULT_FROM_EMAIL')) send_mail( subject=getattr(settings, 'COMPANY_NAME', 'Djing notify'), message=msg_text, diff --git a/djing/local_settings.py.template b/djing/local_settings.py.template index dccee29..88df9a2 100644 --- a/djing/local_settings.py.template +++ b/djing/local_settings.py.template @@ -62,3 +62,10 @@ API_AUTH_SUBNET = '127.0.0.0/8' # Company name COMPANY_NAME = 'Your company name' + +# Email config +EMAIL_HOST_USER = 'YOUR-EMAIL@mailserver.com' +EMAIL_HOST = 'smtp.mailserver.com' +EMAIL_PORT = 587 +EMAIL_HOST_PASSWORD = 'password' +EMAIL_USE_SSL = True diff --git a/djing/settings.py b/djing/settings.py index 02a8a5e..c564215 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -209,3 +209,13 @@ BOOTSTRAP3 = { # Field class to use in horizontal forms 'horizontal_field_class': 'col-md-9', } + + +# Email config +EMAIL_HOST_USER = local_settings.EMAIL_HOST_USER +EMAIL_HOST = local_settings.EMAIL_HOST +EMAIL_PORT = local_settings.EMAIL_PORT +EMAIL_HOST_PASSWORD = local_settings.EMAIL_HOST_PASSWORD +EMAIL_USE_SSL = getattr(local_settings, 'EMAIL_USE_SSL', True) + +SERVER_EMAIL = EMAIL_HOST_USER diff --git a/group_app/views.py b/group_app/views.py index 5aaa5de..8fee7aa 100644 --- a/group_app/views.py +++ b/group_app/views.py @@ -69,7 +69,6 @@ class DeleteGroupView(DeleteView): group_with_subscribers = models.Group.objects.annotate( subscribers_count=Count('abon') ).filter(subscribers_count__gt=0, pk=group_id).first() - print('group_with_subscribers:', group_with_subscribers) if group_with_subscribers is not None: messages.error(request, _('Group is contain subscribers. Remove them before delete group')) return HttpResponseRedirect(self.success_url) From 59c78ded99401d1c050912e765cbf5f8e86ba487 Mon Sep 17 00:00:00 2001 From: www-data Date: Mon, 23 Jul 2018 17:39:01 +0300 Subject: [PATCH 05/16] fix misspelling --- djing/local_settings.py.template | 2 +- djing/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/djing/local_settings.py.template b/djing/local_settings.py.template index 88df9a2..c8beba6 100644 --- a/djing/local_settings.py.template +++ b/djing/local_settings.py.template @@ -68,4 +68,4 @@ EMAIL_HOST_USER = 'YOUR-EMAIL@mailserver.com' EMAIL_HOST = 'smtp.mailserver.com' EMAIL_PORT = 587 EMAIL_HOST_PASSWORD = 'password' -EMAIL_USE_SSL = True +EMAIL_USE_TLS = True diff --git a/djing/settings.py b/djing/settings.py index c564215..d98bba4 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -216,6 +216,6 @@ EMAIL_HOST_USER = local_settings.EMAIL_HOST_USER EMAIL_HOST = local_settings.EMAIL_HOST EMAIL_PORT = local_settings.EMAIL_PORT EMAIL_HOST_PASSWORD = local_settings.EMAIL_HOST_PASSWORD -EMAIL_USE_SSL = getattr(local_settings, 'EMAIL_USE_SSL', True) +EMAIL_USE_TLS = getattr(local_settings, 'EMAIL_USE_TLS', True) SERVER_EMAIL = EMAIL_HOST_USER From e22f75a86b4e44aacd9d32e31bb4be0e5f5e911b Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Mon, 23 Jul 2018 18:37:04 +0300 Subject: [PATCH 06/16] maked daily service type --- abonapp/templates/abonapp/buy_tariff.html | 8 ++++---- abonapp/views.py | 3 +-- tariff_app/custom_tariffs.py | 17 ++++++++++++++++- tariff_app/locale/ru/LC_MESSAGES/django.po | 3 +++ tariff_app/views.py | 4 ++-- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/abonapp/templates/abonapp/buy_tariff.html b/abonapp/templates/abonapp/buy_tariff.html index 67ed5ba..0a8a030 100644 --- a/abonapp/templates/abonapp/buy_tariff.html +++ b/abonapp/templates/abonapp/buy_tariff.html @@ -29,9 +29,9 @@ +