diff --git a/chatbot/models.py b/chatbot/models.py
index fffe6f1..b10f2bd 100644
--- a/chatbot/models.py
+++ b/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()
diff --git a/devapp/views.py b/devapp/views.py
index 9abc4bc..7d13476 100644
--- a/devapp/views.py
+++ b/devapp/views.py
@@ -22,12 +22,11 @@ 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
-
PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
@@ -37,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'
@@ -64,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(user_group=None).only('comment', 'devtype', 'user_group', 'pk', 'ip_address')
@@ -181,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']
@@ -192,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
@@ -468,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 = ' %s' %\
- (_('Device with mac address %(mac)s does not exist') % {'mac': mac})
+ text = ' %s' % \
+ (_('Device with mac address %(mac)s does not exist') % {'mac': mac})
for srcmac, snmpnum in ports:
real_mac = ':'.join(['%x' % ord(i) for i in srcmac])
if mac == real_mac:
@@ -502,7 +509,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
#
diff --git a/djing/global_base_views.py b/djing/global_base_views.py
index 9dd83fe..51040f2 100644
--- a/djing/global_base_views.py
+++ b/djing/global_base_views.py
@@ -1,6 +1,7 @@
from hashlib import sha256
+from json import dumps
from django.views.generic.base import View
-from django.http.response import HttpResponseForbidden, Http404, HttpResponseRedirect
+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
@@ -102,7 +103,22 @@ class OrderingMixin(object):
return "%s%s" % (dfx, order_by)
-class BaseListWithFiltering(ListView):
+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.
@@ -135,9 +151,3 @@ class BaseListWithFiltering(ListView):
'page_number': page_number,
'message': str(e)
})
-
- def get(self, request, *args, **kwargs):
- try:
- return super(BaseListWithFiltering, self).get(request, *args, **kwargs)
- except RedirectWhenError as e:
- return HttpResponseRedirect(e.url)
diff --git a/migrate_to_0.2.py b/migrate_to_0.2.py
new file mode 100755
index 0000000..594d434
--- /dev/null
+++ b/migrate_to_0.2.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+import os
+import sys
+import shutil
+from json import dump
+import django
+
+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.abonlog', models.AbonLog)
+
+ 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)
+
+ 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
+ res = get_fixture_from_unchanget_model('tariff_app.tariff', models.Tariff)
+
+ 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': dv.mac_addr,
+ '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
+ }
+ } 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
+ }
+ } 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
+ 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
+ res = get_fixture_from_unchanget_model('guardian.groupobjectpermission', models.GroupObjectPermission)
+ res += get_fixture_from_unchanget_model('guardian.userobjectpermission', models.UserObjectPermission)
+ 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()
+
+ print('./manage.py loaddata -v2 ...')
+ execute_from_command_line([sys.argv[0], 'loaddata', '-v', '2',
+ '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'
+ ])
+
+
+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()
+ os.remove('fixtures')
+ elif choice == 'makedump':
+ django.setup()
+ make_migration()
+ else:
+ print('Unexpected choice')
diff --git a/static/js/my.js b/static/js/my.js
index 78592fa..953f9bf 100644
--- a/static/js/my.js
+++ b/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);
diff --git a/systemd_units/webdav_backup.py b/systemd_units/webdav_backup.py
index 40982e4..967de77 100644
--- a/systemd_units/webdav_backup.py
+++ b/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))