Browse Source

make map functionality is more usable

devel
Dmitry 8 years ago
parent
commit
377ae2dcbc
  1. 12
      accounts_app/templates/accounts/ext.htm
  2. 14
      accounts_app/templates/accounts/settings/test.html
  3. 8
      bugs.txt
  4. 28
      devapp/forms.py
  5. 72
      devapp/migrations/0004_auto_20171103_0006.py
  6. 53
      devapp/models.py
  7. 84
      devapp/templates/devapp/add_dev.html
  8. 93
      devapp/templates/devapp/dev.html
  9. 24
      devapp/templates/devapp/devices.html
  10. 82
      devapp/templates/devapp/fix_dev_group.html
  11. 33
      devapp/templates/devapp/manage_ports/modal_add_edit_port.html
  12. 1
      devapp/urls.py
  13. 52
      devapp/views.py
  14. 12
      global_context_processors.py
  15. 7
      mapapp/forms.py
  16. 167
      mapapp/locale/ru/LC_MESSAGES/django.po
  17. 45
      mapapp/migrations/0002_auto_20171103_0006.py
  18. 15
      mapapp/models.py
  19. 70
      mapapp/templates/maps/add_device.html
  20. 112
      mapapp/templates/maps/dot.html
  21. 62
      mapapp/templates/maps/index.html
  22. 19
      mapapp/templates/maps/map_tooltip.html
  23. 20
      mapapp/templates/maps/modal_add_device.html
  24. 28
      mapapp/templates/maps/modal_add_dot.html
  25. 38
      mapapp/templates/maps/options.html
  26. 7
      mapapp/templates/maps/preload_devices_tmpl.html
  27. 120
      mapapp/templates/maps/ya_index.html
  28. 6
      mapapp/urls.py
  29. 149
      mapapp/views.py
  30. 5
      requirements.txt
  31. 48
      static/clientside/ISPlaylist.m3u
  32. 22
      static/css/custom.css
  33. BIN
      static/img/loading.gif
  34. 60
      static/js/my.js
  35. 20
      systemd_units/do_backup.sh
  36. 20
      systemd_units/webdav_backup.py
  37. 2
      taskapp/templates/taskapp/add_edit_task.html
  38. 8
      templates/403.html
  39. 12
      templates/403_for_modal.html
  40. 10
      templates/all_base.html
  41. 2
      templates/base.html

12
accounts_app/templates/accounts/ext.htm

@ -20,23 +20,23 @@
{% else %}
<img alt="ava" src="/static/img/user_ava.gif"/>
{% endif %}
<div class="caption btn-group btn-group-sm">
<div class="caption btn-group btn-group-sm btn-group-justified">
{% if userprofile == request.user %}
<a href="{% url 'acc_app:setup_info' %}" class="btn btn-primary" role="button">
<a href="{% url 'acc_app:setup_info' %}" class="btn btn-primary">
<span class="glyphicon glyphicon-edit"></span>
{% trans 'Edit' %}
<span class="hidden-sm hidden-md">{% trans 'Edit' %}</span>
</a>
{% endif %}
{% if request.user.is_superuser %}
{% if userprofile.is_superuser %}
<a href="{% url 'acc_app:setup_perms' userprofile.pk %}" class="btn btn-default" title="{% trans 'Profile is superuser, permissions to change it makes no sense' %}" data-toggle="tooltip">
<span class="glyphicon glyphicon-lock"></span>
{% trans 'Permission options' %}
<span class="hidden-sm hidden-md">{% trans 'Permission options' %}</span>
</a>
{% else %}
<a href="{% url 'acc_app:setup_perms' userprofile.pk %}" class="btn btn-default btn-sm">
<a href="{% url 'acc_app:setup_perms' userprofile.pk %}" class="btn btn-default">
<span class="glyphicon glyphicon-lock"></span>
{% trans 'Permission options' %}
<span class="hidden-sm hidden-md">{% trans 'Permission options' %}</span>
</a>
{% endif %}
{% endif %}

14
accounts_app/templates/accounts/settings/test.html

@ -1,14 +0,0 @@
<p>
<label for="id_permissions">Permissions:</label>
<select multiple="multiple" id="id_permissions" name="permissions">
<option value="add_abongroup">Can add abon group</option>
<option value="can_add_ballance">Пополнение счёта</option>
<option value="can_view_abongroup" selected="selected">Can view subscriber group</option>
<option value="change_abongroup">Can change abon group</option>
<option value="delete_abongroup">Can delete abon group</option>
</select>
</p>

8
bugs.txt

@ -1,8 +0,0 @@
- (GUI) Иконки возле кнопок не настроены, натыканы случайно
- В abonapp.complete_service нельзя досрочно завершить услугу пока нет связи с NAS'ом
- Надо указывать в /usr/lib/python3.5/site-packages/django/contrib/auth/decorators.py raise_exception=True
- Пароли абонентов надо шифровать ключом для паролей
- Доделать везде переводы
- Не надо коннектиться к микротику когда не собираемся ничего изменять. А то при сохранении залогинились и вышли без действий
- Не удаляет просроченные услуги если не пингуется NAS
! Проверить дату завершения услуги

28
devapp/forms.py

@ -11,8 +11,7 @@ from djing import MAC_ADDR_REGEX
class DeviceForm(forms.ModelForm):
mac_addr = forms.CharField(widget=forms.TextInput(attrs={
'pattern': MAC_ADDR_REGEX,
'required': True,
'class': 'form-control'
'required': True
}), error_messages={
'required': _('Mac address is required for fill'),
'unique': _('Device with that mac is already exist')
@ -20,31 +19,18 @@ class DeviceForm(forms.ModelForm):
class Meta:
model = models.Device
fields = '__all__'
exclude = ['map_dot']
widgets = {
'ip_address': forms.TextInput(attrs={
'pattern': ip_addr_regex,
'placeholder': '192.168.0.100',
'class': 'form-control'
'placeholder': '192.168.0.100'
}),
'comment': forms.TextInput(attrs={
'required': True,
'class': 'form-control'
}),
'devtype': forms.Select(attrs={
'class': 'form-control'
}),
'man_passw': forms.PasswordInput(attrs={
'class': 'form-control'
}, render_value=True),
'map_dot': forms.Select(attrs={
'class': 'form-control'
'required': True
}),
'man_passw': forms.PasswordInput(render_value=True),
'user_group': forms.Select(attrs={
'class': 'form-control'
}),
'parent_dev': forms.Select(attrs={
'class': 'form-control'
})
}
@ -55,11 +41,7 @@ class PortForm(forms.ModelForm):
exclude = ['device']
widgets = {
'num': forms.NumberInput(attrs={
'class': 'form-control',
'min': '0'
}),
'descr': forms.TextInput(attrs={
'class': 'form-control'
})
}

72
devapp/migrations/0004_auto_20171103_0006.py

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-11-03 00:06
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import djing.fields
import mydefs
class Migration(migrations.Migration):
dependencies = [
('devapp', '0003_auto_20170927_1838'),
]
operations = [
migrations.RemoveField(
model_name='device',
name='map_dot',
),
migrations.AlterField(
model_name='device',
name='comment',
field=models.CharField(max_length=256, verbose_name='Comment'),
),
migrations.AlterField(
model_name='device',
name='devtype',
field=models.CharField(choices=[('Dl', 'DLink switch'), ('Pn', 'PON OLT'), ('On', 'PON ONU'), ('Ex', 'Eltex switch')], default='Dl', max_length=2, verbose_name='Device type'),
),
migrations.AlterField(
model_name='device',
name='ip_address',
field=mydefs.MyGenericIPAddressField(max_length=8, protocol='ipv4', verbose_name='Ip address'),
),
migrations.AlterField(
model_name='device',
name='mac_addr',
field=djing.fields.MACAddressField(blank=True, integer=True, null=True, unique=True, verbose_name='Mac address'),
),
migrations.AlterField(
model_name='device',
name='man_passw',
field=models.CharField(blank=True, max_length=16, null=True, verbose_name='SNMP password'),
),
migrations.AlterField(
model_name='device',
name='parent_dev',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='devapp.Device', verbose_name='Parent device'),
),
migrations.AlterField(
model_name='device',
name='user_group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup', verbose_name='User group'),
),
migrations.AlterField(
model_name='port',
name='descr',
field=models.CharField(blank=True, max_length=60, null=True, verbose_name='Description'),
),
migrations.AlterField(
model_name='port',
name='device',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device', verbose_name='Device'),
),
migrations.AlterField(
model_name='port',
name='num',
field=models.PositiveSmallIntegerField(default=0, verbose_name='Number'),
),
]

53
devapp/models.py

@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
import requests
from django.db import models
from djing.fields import MACAddressField
from .base_intr import DevBase
from mydefs import MyGenericIPAddressField, MyChoicesAdapter
from mydefs import MyGenericIPAddressField, MyChoicesAdapter, ip2int
from . import dev_types
from mapapp.models import Dot
from subprocess import run
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from json.decoder import JSONDecodeError
DEVICE_TYPES = (
@ -23,15 +23,34 @@ class DeviceDBException(Exception):
pass
class DeviceManager(models.Manager):
@staticmethod
def wrap_monitoring_info(devices_queryset):
nag_url = getattr(settings, 'NAGIOS_URL')
if nag_url:
addrs = ['h=%s' % hex(ip2int(dev.ip_address))[2:] for dev in devices_queryset]
url = '%s/host/status/arr?%s' % (nag_url, '&'.join(addrs))
try:
res = requests.get(url).json()
except (requests.exceptions.ConnectionError, JSONDecodeError):
return
for dev in devices_queryset:
inf = [x for x in res if x.get('address') == dev.ip_address]
if len(inf) > 0:
setattr(dev, 'mon', inf[0].get('current_status'))
return devices_queryset
class Device(models.Model):
ip_address = MyGenericIPAddressField()
mac_addr = MACAddressField(null=True, blank=True, unique=True)
comment = models.CharField(max_length=256)
devtype = models.CharField(max_length=2, default=DEVICE_TYPES[0][0], choices=MyChoicesAdapter(DEVICE_TYPES))
man_passw = models.CharField(max_length=16, null=True, blank=True)
map_dot = models.ForeignKey(Dot, on_delete=models.SET_NULL, null=True, blank=True)
user_group = models.ForeignKey('abonapp.AbonGroup', on_delete=models.SET_NULL, null=True, blank=True)
parent_dev = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL)
ip_address = MyGenericIPAddressField(verbose_name=_('Ip address'))
mac_addr = MACAddressField(verbose_name=_('Mac address'), null=True, blank=True, unique=True)
comment = models.CharField(_('Comment'), max_length=256)
devtype = models.CharField(_('Device type'), max_length=2, default=DEVICE_TYPES[0][0], choices=MyChoicesAdapter(DEVICE_TYPES))
man_passw = models.CharField(_('SNMP password'), max_length=16, null=True, blank=True)
user_group = models.ForeignKey('abonapp.AbonGroup', verbose_name=_('User group'), on_delete=models.SET_NULL, null=True, blank=True)
parent_dev = models.ForeignKey('self', verbose_name=_('Parent device'), blank=True, null=True, on_delete=models.SET_NULL)
objects = DeviceManager()
class Meta:
db_table = 'dev'
@ -44,8 +63,10 @@ class Device(models.Model):
def get_abons(self):
pass
def get_stat(self):
pass
def get_status(self):
url = getattr(settings, 'NAGIOS_URL')
if url:
return requests.get('%s/host/status?addr=%s' % (url, self.ip_address))
def get_manager_klass(self):
klasses = [kl for kl in DEVICE_TYPES if kl[0] == self.devtype]
@ -65,9 +86,9 @@ class Device(models.Model):
class Port(models.Model):
device = models.ForeignKey(Device)
num = models.PositiveSmallIntegerField(default=0)
descr = models.CharField(max_length=60, null=True, blank=True)
device = models.ForeignKey(Device, verbose_name=_('Device'))
num = models.PositiveSmallIntegerField(_('Number'), default=0)
descr = models.CharField(_('Description'), max_length=60, null=True, blank=True)
def __str__(self):
return "%d: %s" % (int(self.num), self.descr)

84
devapp/templates/devapp/add_dev.html

@ -1,5 +1,6 @@
{% extends request.is_ajax|yesno:'bajax.html,base.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block main %}
<ol class="breadcrumb">
@ -23,76 +24,23 @@
<form role="form" action="{% url 'devapp:add' group.pk %}" method="post" autocomplete="off">{% csrf_token %}
<div class="form-group">
<label for="id_ip_address">{% trans 'Ip address' %}</label>
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.ip_address addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-globe"></span></span>
{{ form.ip_address }}{{ form.ip_address.errors }}
</div>
</div>
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.mac_addr addon_before=ic %}
<div class="form-group">
<label for="id_mac_addr">{% trans 'Mac address' %}</label>
<div class="input-group{% if form.mac_addr.errors %} has-error{% endif %}">
<span class="input-group-addon"><span class="glyphicon glyphicon-globe"></span></span>
{{ form.mac_addr }}
{% if already_dev %}
<span class="input-group-btn">
<a class="btn btn-danger" href="{% url 'devapp:view' group.pk already_dev.pk %}" title="{% trans 'View the device' %}" data-toggle="tooltip">
{{ already_dev.comment }}
</a>
</span>
{% endif %}
</div>
{{ form.mac_addr.errors }}
</div>
{% bootstrap_icon 'comment' as ic %}
{% bootstrap_field form.comment addon_before=ic %}
<div class="form-group">
<label for="id_comment">{% trans 'Comment' %}</label>
{% bootstrap_icon 'hdd' as ic %}
{% bootstrap_field form.devtype addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-comment"></span></span>
{{ form.comment }}{{ form.comment.errors }}
</div>
</div>
<div class="form-group">
<label for="id_devtype">{% trans 'Device type' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-hdd"></span></span>
{{ form.devtype }}{{ form.devtype.errors }}
</div>
</div>
{% bootstrap_icon 'lock' as ic %}
{% bootstrap_field form.man_passw addon_before=ic %}
<div class="form-group">
<label for="id_man_passw">{% trans 'SNMP password' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
{{ form.man_passw }}{{ form.man_passw.errors }}
</div>
</div>
<div class="form-group">
<label for="id_map_dot">{% trans 'Map point' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-map-marker"></span></span>
{{ form.map_dot }}{{ form.devtype.errors }}
</div>
</div>
<div class="form-group">
<label for="id_user_group">{% trans 'User group' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-subscript"></span></span>
{{ form.user_group }}{{ form.user_group.errors }}
</div>
</div>
{% bootstrap_icon 'subscript' as ic %}
{% bootstrap_field form.user_group addon_before=ic %}
<div class="form-group">
<label for="id_parent_dev">{% trans 'Parent device' %}</label>
@ -106,11 +54,11 @@
</div>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-sm btn-primary">
<div class="btn-group btn-group-sm">
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
<button type="reset" class="btn btn-sm btn-default">
<button type="reset" class="btn btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %}
</button>
</div>

93
devapp/templates/devapp/dev.html

@ -1,5 +1,6 @@
{% extends request.is_ajax|yesno:'nullcont.htm,devapp/ext.htm' %}
{% load i18n %}
{% load bootstrap3 %}
{% block content %}
<div class="panel panel-default">
@ -10,91 +11,43 @@
<form role="form" action="{% url 'devapp:edit' group.pk|default:0 dev.pk %}" method="post">{% csrf_token %}
<div class="form-group">
<label for="id_ip_address">{% trans 'Ip address' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-globe"></span></span>
{{ form.ip_address }}{{ form.ip_address.errors }}
</div>
</div>
<div class="form-group">
<label for="id_mac_addr">{% trans 'Mac address' %}</label>
<div class="input-group{% if form.mac_addr.errors %} has-error{% endif %}">
<span class="input-group-addon"><span class="glyphicon glyphicon-globe"></span></span>
{{ form.mac_addr }}
{% if already_dev %}
<span class="input-group-btn">
<a class="btn btn-danger" href="{% url 'devapp:view' group.pk already_dev.pk %}" title="{% trans 'View the device' %}" data-toggle="tooltip">
{{ already_dev.comment }}
</a>
</span>
{% endif %}
</div>
{{ form.mac_addr.errors }}
</div>
<div class="form-group">
<label for="id_comment">{% trans 'Comment' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-comment"></span></span>
{{ form.comment }}{{ form.comment.errors }}
</div>
</div>
<div class="form-group">
<label for="id_devtype">{% trans 'Device type' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-hdd"></span></span>
{{ form.devtype }}{{ form.devtype.errors }}
</div>
</div>
<div class="form-group">
<label for="id_man_passw">{% trans 'SNMP password' %}</label>
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.ip_address addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
{{ form.man_passw }}{{ form.man_passw.errors }}
</div>
</div>
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.mac_addr addon_before=ic %}
<div class="form-group">
<label for="id_map_dot">{% trans 'Map point' %}</label>
{% bootstrap_icon 'comment' as ic %}
{% bootstrap_field form.comment addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-map-marker"></span></span>
{{ form.map_dot }}{{ form.devtype.errors }}
</div>
</div>
{% bootstrap_icon 'hdd' as ic %}
{% bootstrap_field form.devtype addon_before=ic %}
<div class="form-group">
<label for="id_user_group">{% trans 'User group' %}</label>
{% bootstrap_icon 'lock' as ic %}
{% bootstrap_field form.man_passw addon_before=ic %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-subscript"></span></span>
{{ form.user_group }}{{ form.user_group.errors }}
</div>
</div>
{% bootstrap_icon 'subscript' as ic %}
{% bootstrap_field form.user_group addon_before=ic %}
<div class="form-group">
<label for="id_parent_dev">{% trans 'Parent device' %}</label>
<div class="input-group selectajax" data-dst="/dev/search_dev">
<span class="input-group-addon"><span class="glyphicon glyphicon-hdd"></span></span>
<input type="hidden" name="parent_dev" class="selectajax-hid"{% if selected_parent_dev %} value="{{ selected_parent_dev.pk }}"{% endif %}>
<input type="hidden" name="parent_dev" class="selectajax-hid" {% if selected_parent_dev %}
value="{{ selected_parent_dev.pk }}" {% endif %}>
{% if selected_parent_dev %}
<button class="selectajax-btn form-control btn btn-default">{{ selected_parent_dev.comment }}</button>
<input type="text" class="form-control dropdown-toggle selectajax-inp hidden" data-toggle="dropdown" id="id_parent_dev" placeholder="{% trans 'Find the device' %}">
<button class="selectajax-btn form-control btn btn-default">{{ selected_parent_dev.comment }}
</button>
<input type="text" class="form-control dropdown-toggle selectajax-inp hidden" data-toggle="dropdown"
id="id_parent_dev" placeholder="{% trans 'Find the device' %}">
{% else %}
<button class="selectajax-btn form-control btn btn-default hidden"></button>
<input type="text" class="form-control dropdown-toggle selectajax-inp" data-toggle="dropdown" id="id_parent_dev" placeholder="{% trans 'Find the device' %}">
<input type="text" class="form-control dropdown-toggle selectajax-inp" data-toggle="dropdown"
id="id_parent_dev" placeholder="{% trans 'Find the device' %}">
{% endif %}
<ul class="dropdown-menu selectajax-ul"></ul>{{ form.parent_dev.errors }}
<ul class="dropdown-menu selectajax-ul"></ul>
{{ form.parent_dev.errors }}
</div>
</div>

24
devapp/templates/devapp/devices.html

@ -15,21 +15,23 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>#</th>
<th>
<a href="{% url 'devapp:devs' group.pk %}?order_by=ip_address&dir={{ dir|default:"down" }}">
<a href="{% url 'devapp:devs' group.pk %}?order_by=ip_address&dir={{ dir|default:'down' }}">
{% trans 'Ip address' %}
</a>
{% if order_by == 'ip_address' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th>
<a href="{% url 'devapp:devs' group.pk %}?order_by=comment&dir={{ dir|default:"down" }}">
<a href="{% url 'devapp:devs' group.pk %}?order_by=comment&dir={{ dir|default:'down' }}">
{% trans 'Comment' %}
</a>
{% if order_by == 'comment' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th>{% trans 'Mac address' %}</th>
<th width="250">
<a href="{% url 'devapp:devs' group.pk %}?order_by=devtype&dir={{ dir|default:"down" }}">
<th>{% trans 'Monitoring output' %}</th>
<th>
<a href="{% url 'devapp:devs' group.pk %}?order_by=devtype&dir={{ dir|default:'down' }}">
{% trans 'Device type' %}
</a>
{% if order_by == 'devtype' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
@ -42,9 +44,17 @@
{% with can_del_dev=perms.devapp.delete_device can_ch_dev=perms.devapp.change_device %}
{% for dev in devices %}
<tr>
<td>
{% if dev.mon.current_state == '0' %}
<span class="glyphicon glyphicon-ok-circle text-success"></span>
{% else %}
<span class="glyphicon glyphicon-exclamation-sign text-danger"></span>
{% endif %}
</td>
<td><a href="{% url 'devapp:view' dev.user_group.pk dev.pk %}">{{ dev.ip_address }}</a></td>
<td>{{ dev.comment }}</td>
<td>{{ dev.mac_addr }}</td>
<td>{{ dev.mac_addr|default:_('Not assigned') }}</td>
<td>{{ dev.mon.plugin_output }}</td>
<td>{{ dev.get_devtype_display }}</td>
<td class="btn-group btn-group-sm">
{% if can_del_dev %}
@ -61,7 +71,7 @@
</tr>
{% empty %}
<tr>
<td colspan="5">{% trans 'Devices does not found' %}. <a href="{% url 'devapp:add' group.pk %}">{% trans 'Create' %}</a></td>
<td colspan="7">{% trans 'Devices does not found' %}. <a href="{% url 'devapp:add' group.pk %}">{% trans 'Create' %}</a></td>
</tr>
{% endfor %}
{% endwith %}
@ -69,7 +79,7 @@
<tfoot>
<tr>
<td colspan="5">
<td colspan="7">
<a href="{% url 'devapp:add' group.pk %}" class="btn btn-success btn-sm">
<span class="glyphicon glyphicon-plus"></span> {% trans 'Create' %}
</a>

82
devapp/templates/devapp/fix_dev_group.html

@ -0,0 +1,82 @@
{% extends request.is_ajax|yesno:'bajax.html,base.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'devapp:group_list' %}">{% trans 'Groups' %}</a></li>
<li class="active">{{ dev.comment }}</li>
</ol>
{% include 'message_block.html' %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Fix device group' %}</h3>
</div>
<div class="panel-body">
<form role="form" action="{% url 'devapp:fix_device_group' dev.pk %}" method="post">{% csrf_token %}
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.ip_address addon_before=ic %}
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.mac_addr addon_before=ic %}
{% bootstrap_icon 'comment' as ic %}
{% bootstrap_field form.comment addon_before=ic %}
{% bootstrap_icon 'hdd' as ic %}
{% bootstrap_field form.devtype addon_before=ic %}
{% bootstrap_icon 'lock' as ic %}
{% bootstrap_field form.man_passw addon_before=ic %}
<div class="form-group">
<label class="control-label" for="{{ form.user_group.id_for_label }}">{{ form.user_group.label }}</label>
<div class="input-group{% if not dev.user_group %} has-error{% endif %}">
<span class="input-group-addon">
{% bootstrap_icon 'subscript' %}
</span>
{{ form.user_group }}
</div>
</div>
<div class="form-group">
<label for="id_parent_dev">{% trans 'Parent device' %}</label>
<div class="input-group selectajax" data-dst="/dev/search_dev">
<span class="input-group-addon"><span class="glyphicon glyphicon-hdd"></span></span>
<input type="hidden" name="parent_dev" class="selectajax-hid" {% if selected_parent_dev %}
value="{{ selected_parent_dev.pk }}" {% endif %}>
{% if selected_parent_dev %}
<button class="selectajax-btn form-control btn btn-default">{{ selected_parent_dev.comment }}</button>
<input type="text" class="form-control dropdown-toggle selectajax-inp hidden" data-toggle="dropdown"
id="id_parent_dev" placeholder="{% trans 'Find the device' %}">
{% else %}
<button class="selectajax-btn form-control btn btn-default hidden"></button>
<input type="text" class="form-control dropdown-toggle selectajax-inp" data-toggle="dropdown"
id="id_parent_dev" placeholder="{% trans 'Find the device' %}">
{% endif %}
<ul class="dropdown-menu selectajax-ul"></ul>
{{ form.parent_dev.errors }}
</div>
</div>
<div class="btn-group btn-group-sm">
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
<button type="reset" class="btn btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %}
</button>
</div>
</form>
</div>
</div>
{% endblock %}

33
devapp/templates/devapp/manage_ports/modal_add_edit_port.html

@ -1,9 +1,14 @@
{% load i18n %}
{% load i18n %}{% load bootstrap3 %}
{% if port_id %}
<form role="form" action="{% url 'devapp:edit_port' gid did port_id %}" method="post">{% else %}
<form role="form" action="{% url 'devapp:add_port' gid did %}" method="post">{% endif %}{% csrf_token %}
{% url 'devapp:edit_port' gid did port_id as frm_url %}
{% else %}
{% url 'devapp:add_port' gid did as frm_url %}
{% endif %}
<form role="form" action="{{ frm_url }}" method="post">{% csrf_token %}
<input type="hidden" value="yes" name="confirm">
<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-exclamation-sign"></span>{% trans 'Are you sure?' %}</h4>
@ -11,27 +16,11 @@
<div class="modal-body">
<div class="form-group">
<label for="id_num">{% trans 'Number' %}</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-bishop"></span></span>
{{ form.num }}{{ form.num.errors }}
</div>
</div>
<div class="form-group">
<label for="id_descr">{% trans 'Description' %}</label>
{% bootstrap_form form %}
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-comment"></span></span>
{{ form.descr }}{{ form.descr.errors }}
</div>
</div>
{% trans 'Save' as btntxt %}
{% bootstrap_button btntxt button_type="submit" button_class="btn-primary" icon="save" size='sm' %}
<button type="submit" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
</div>
</form>

1
devapp/urls.py

@ -17,5 +17,6 @@ urlpatterns = [
url(r'^(\d+)/(?P<did>\d+)/(?P<portid>\d+)_(?P<status>[0-1]{1})$', views.toggle_port, name='port_toggle'),
url(r'^(?P<grp>\d+)/(?P<did>\d+)/(?P<portid>\d+)/del$', views.delete_single_port, name='del_port'),
url(r'^(?P<grp>\d+)/(?P<did>\d+)/(?P<pid>\d+)/edit$', views.edit_single_port, name='edit_port'),
url(r'^fix_device_group/(?P<did>\d+)$', views.fix_device_group, name='fix_device_group'),
url(r'^search_dev$', views.search_dev)
]

52
devapp/views.py

@ -25,8 +25,7 @@ def devices(request, grp):
group = get_object_or_404(AbonGroup, pk=grp)
if not request.user.has_perm('abonapp.can_view_abongroup', group):
raise PermissionDenied
devs = Device.objects.filter(user_group=grp).only('comment', 'mac_addr', 'devtype', 'user_group', 'pk',
'ip_address')
devs = Device.objects.filter(user_group=grp).select_related('user_group').only('comment', 'mac_addr', 'devtype', 'user_group', 'pk', 'ip_address')
# фильтр
dr, field = order_helper(request)
@ -36,7 +35,7 @@ def devices(request, grp):
devs = pag_mn(request, devs)
return render(request, 'devapp/devices.html', {
'devices': devs,
'devices': Device.objects.wrap_monitoring_info(devs),
'dir': dr,
'order_by': request.GET.get('order_by'),
'group': group
@ -95,10 +94,16 @@ def dev(request, grp, devid=0):
if frm.is_valid():
ndev = frm.save()
messages.success(request, _('Device info has been saved'))
return redirect('devapp:edit', grp, ndev.pk)
return redirect('devapp:edit', ndev.user_group.pk, ndev.pk)
else:
try:
already_dev = Device.objects.get(mac_addr=request.POST.get('mac_addr'))
if already_dev.user_group:
messages.warning(request, _('You have redirected to existing device'))
return redirect('devapp:view', already_dev.user_group.pk, already_dev.pk)
else:
messages.warning(request, _('Please attach user group for device'))
return redirect('devapp:fix_device_group', already_dev.pk)
except Device.DoesNotExist:
pass
messages.error(request, _('Form is invalid, check fields and try again'))
@ -125,7 +130,7 @@ def dev(request, grp, devid=0):
return render(request, 'devapp/dev.html', {
'form': frm,
'dev': devinst,
'selected_parent_dev': devinst.parent_dev or None,
'selected_parent_dev': devinst.parent_dev,
'group': user_group,
'already_dev': already_dev
})
@ -138,7 +143,7 @@ def manage_ports(request, devid):
dev = Device.objects.get(pk=devid)
if dev.user_group is None:
messages.error(request, _('Device is not have a group, please fix that'))
return redirect('devapp:group_list')
return redirect('devapp:fix_device_group', dev.pk)
ports = Port.objects.filter(device=dev)
except Device.DoesNotExist:
@ -177,7 +182,7 @@ def add_ports(request, devid):
dev = Device.objects.get(pk=devid)
if dev.user_group is None:
messages.error(request, _('Device is not have a group, please fix that'))
return redirect('devapp:group_list')
return redirect('devapp:fix_device_group', dev.pk)
if request.method == 'POST':
ports = zip(
request.POST.getlist('p_text'),
@ -306,6 +311,11 @@ def devview(request, did):
ports = None
uptime = 0
dev = get_object_or_404(Device, id=did)
if not dev.user_group:
messages.warning(request, _('Please attach user group for device'))
return redirect('devapp:fix_device_group', dev.pk)
template_name = 'ports.html'
try:
if ping(dev.ip_address):
@ -354,6 +364,8 @@ def toggle_port(request, did, portid, status=0):
messages.error(request, _('Dot was not pinged'))
except EasySNMPTimeoutError:
messages.error(request, _('wait for a reply from the SNMP Timeout'))
except EasySNMPError as e:
messages.error(request, e)
return redirect('devapp:view', dev.user_group.pk if dev.user_group is not None else 0, did)
@ -378,3 +390,29 @@ def search_dev(request):
).only('pk', 'ip_address', 'comment')[:16]
results = [{'id': dev.pk, 'text': "%s: %s" % (dev.ip_address, dev.comment)} for dev in results]
return HttpResponse(dumps(results, ensure_ascii=False))
@login_required
def fix_device_group(request, did):
dev = get_object_or_404(Device, pk=did)
try:
if request.method == 'POST':
frm = DeviceForm(request.POST, instance=dev)
if frm.is_valid():
ch_dev = frm.save()
if ch_dev.user_group:
messages.success(request, _('Device fixed'))
return redirect('devapp:devs', ch_dev.user_group.pk)
else:
messages.error(request, _('Please attach user group for device'))
else:
messages.error(request, _('Form is invalid, check fields and try again'))
else:
frm = DeviceForm(instance=dev)
except ValueError:
return HttpResponse('ValueError')
return render(request, 'devapp/fix_dev_group.html', {
'form': frm,
'dev': dev,
'selected_parent_dev': dev.parent_dev
})

12
global_context_processors.py

@ -1,18 +1,12 @@
# -*- coding: utf-8 -*-
from django.shortcuts import get_object_or_404
from abonapp.models import Abon
#def context_processor_client_ipaddress(request):
# ip = request.META.get('REMOTE_ADDR', '') or request.META.get('HTTP_X_FORWARDED_FOR', '')
# return {
# 'client_ipaddress': ip
# }
from django.conf import settings
# От сюда можно получать на клиентской стороне профиль абонента
def context_processor_additional_profile(request):
if request.user.is_staff or request.user.is_anonymous():
return {'subscriber': request.user}
return {'subscriber': request.user, 'FILE_UPLOAD_MAX_MEMORY_SIZE': settings.FILE_UPLOAD_MAX_MEMORY_SIZE}
else:
return {'subscriber': get_object_or_404(Abon, id=request.user.pk)}
return {'subscriber': get_object_or_404(Abon, id=request.user.pk), 'FILE_UPLOAD_MAX_MEMORY_SIZE': settings.FILE_UPLOAD_MAX_MEMORY_SIZE}

7
mapapp/forms.py

@ -6,9 +6,8 @@ from .models import Dot
class DotForm(forms.ModelForm):
class Meta:
model = Dot
fields = '__all__'
exclude = ['devices']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control', 'required': '', 'autofocus':''}),
'latitude': forms.NumberInput(attrs={'class': 'form-control', 'required': ''}),
'longitude': forms.NumberInput(attrs={'class': 'form-control', 'required': ''})
'title': forms.TextInput(attrs={'required': '', 'autofocus': ''}),
}

167
mapapp/locale/ru/LC_MESSAGES/django.po

@ -0,0 +1,167 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Dmitry Novikov nerosketch@gmail.com, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-11-03 00:12+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n"
#: models.py:7
msgid "Map point title"
msgstr "Название точки"
#: models.py:8
msgid "Latitude"
msgstr "Широта"
#: models.py:9 templates/maps/options.html.py:18
msgid "Longitude"
msgstr "Долгота"
#: models.py:10 templates/maps/dot.html.py:63
msgid "Devices"
msgstr "Устройства"
#: models.py:11
msgid "Attachment"
msgstr "Приложение"
#: models.py:15
msgid "Map point"
msgstr "Геоточка"
#: models.py:16
msgid "Map points"
msgstr "Геоточки"
#: models.py:18
msgid "Can view"
msgstr "Может просматривать"
#: templates/maps/add_device.html:8 templates/maps/dot.html.py:8
#: templates/maps/options.html:7
msgid "Map settings"
msgstr "Настройки карты"
#: templates/maps/add_device.html:10
msgid "Add devices"
msgstr "Добавить устройства"
#: templates/maps/add_device.html:24
msgid "Pick the group"
msgstr "Выбрать группу"
#: templates/maps/add_device.html:55
msgid "Select the devices"
msgstr "Выбрать устройства"
#: templates/maps/add_device.html:62 templates/maps/dot.html.py:49
#: templates/maps/modal_add_device.html:15 templates/maps/modal_add_dot.html:25
msgid "Save"
msgstr "Сохранить"
#: templates/maps/dot.html:13
msgid "Add new point"
msgstr "Добавить новую точку"
#: templates/maps/dot.html:52
msgid "Reset"
msgstr "Сбросить"
#: templates/maps/dot.html:83
msgid "User group has no attached"
msgstr "Группа абонентов не привязана"
#: templates/maps/dot.html:89
msgid "Devices not found"
msgstr "Устройства не найдены"
#: templates/maps/dot.html:94
msgid "Add"
msgstr "Добавить"
#: templates/maps/map_tooltip.html:15
msgid "Pinned devices not found"
msgstr "Привязанные устройства не найдены"
#: templates/maps/modal_add_device.html:5
msgid "Add device"
msgstr "Добавить устройство"
#: templates/maps/modal_add_device.html:17
msgid "Close"
msgstr "Закрыть"
#: templates/maps/modal_add_dot.html:6
msgid "Add point"
msgstr "Добавить точку"
#: templates/maps/modal_add_dot.html:9
#, python-format
msgid "Coords: %(coords)s"
msgstr "Координаты %(coords)s"
#: templates/maps/options.html:17
msgid "Title"
msgstr "Название"
#: templates/maps/options.html:32
msgid "Edit"
msgstr "Изменить"
#: templates/maps/options.html:39
msgid "Delete"
msgstr "Удалить"
#: templates/maps/options.html:49
msgid "You have not created map points yet"
msgstr "Вы ещё не создали ни одной геоточки"
#: templates/maps/options.html:49
msgid "Create"
msgstr "Создать"
#: templates/maps/preload_devices_tmpl.html:7
msgid "no devices found"
msgstr "нет усройств"
#: templates/maps/ya_index.html:11
msgid "Loading.."
msgstr "Загрузка.."
#: templates/maps/ya_index.html:144
msgid "Layers"
msgstr "Слои"
#: templates/maps/ya_index.html:153
msgid "Show all"
msgstr "Показать всё"
#: views.py:53
msgid "Map point has been saved"
msgstr "Геоточка сохранена"
#: views.py:56 views.py:111
msgid "fix form errors"
msgstr "исправте ошибки формы"
#: views.py:66 views.py:79
msgid "Map point does not exist"
msgstr "Геоточка не найдена"
#: views.py:77
#, python-format
msgid "Map point '%(title)s' has been deleted"
msgstr "Геоточка '%(title)s' была удалена"

45
mapapp/migrations/0002_auto_20171103_0006.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-11-03 00:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('devapp', '0004_auto_20171103_0006'),
('mapapp', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='dot',
options={'permissions': (('can_view', 'Can view'),), 'verbose_name': 'Map dot', 'verbose_name_plural': 'Map dots'},
),
migrations.AddField(
model_name='dot',
name='attachment',
field=models.FileField(blank=True, null=True, upload_to='map_attachments/%Y_%m_%d', verbose_name='Attachment'),
),
migrations.AddField(
model_name='dot',
name='devices',
field=models.ManyToManyField(db_table='dot_device', to='devapp.Device', verbose_name='Devices'),
),
migrations.AlterField(
model_name='dot',
name='latitude',
field=models.FloatField(verbose_name='Latitude'),
),
migrations.AlterField(
model_name='dot',
name='longitude',
field=models.FloatField(verbose_name='Longitude'),
),
migrations.AlterField(
model_name='dot',
name='title',
field=models.CharField(max_length=127, verbose_name='Map point title'),
),
]

15
mapapp/models.py

@ -1,13 +1,22 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from devapp.models import Device
class Dot(models.Model):
title = models.CharField(max_length=127)
latitude = models.FloatField()
longitude = models.FloatField()
title = models.CharField(_('Map point title'), max_length=127)
latitude = models.FloatField(_('Latitude'))
longitude = models.FloatField(_('Longitude'))
devices = models.ManyToManyField(Device, verbose_name=_('Devices'), db_table='dot_device')
attachment = models.FileField(_('Attachment'), upload_to='map_attachments/%Y_%m_%d', null=True, blank=True)
class Meta:
db_table = 'dots'
verbose_name = _('Map point')
verbose_name_plural = _('Map points')
permissions = (
('can_view', _('Can view')),
)
def __str__(self):
return self.title

70
mapapp/templates/maps/add_device.html

@ -0,0 +1,70 @@
{% extends 'base.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'mapapp:options' %}">{% trans 'Map settings' %}</a></li>
<li><a href="{% url 'mapapp:edit_dot' dot.pk %}">{{ dot.title }}</a></li>
<li class="active">{% trans 'Add devices' %}</li>
</ol>
{% include 'message_block.html' %}
<script type="text/javascript">
</script>
<div class="row">
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Pick the group' %}</h3>
</div>
<div class="panel-body">
{% url 'mapapp:preload_devices' as prel_url %}
{% for group in groups %}
{% if group.pk == grp %}
<u><a href="#" data-href="{{ prel_url }}?dot={{ dot.pk }}&grp={{ group.pk }}" class="btn_ajloader">{{ group.title }}</a></u>
{% else %}
<a href="#" data-href="{{ prel_url }}?dot={{ dot.pk }}&grp={{ group.pk }}" class="btn_ajloader">{{ group.title }}</a>
{% endif %}<br>
{% endfor %}
</div>
</div>
<script type="text/javascript">
$(window).load(function() {
$('.btn_ajloader').on('click', function(e){
var grp_id = $(this).attr('data-href');
grp_id = grp_id.split('=');
grp_id = grp_id[grp_id.length-1];
$('#selected_user_group').val(grp_id);
e.preventDefault();
});
});
</script>
</div>
<div class="col-sm-8">
<form role="form" action="{% url 'mapapp:add_dev' dot.pk %}?grp={{ grp }}" method="post">{% csrf_token %}
<input type="hidden" id="selected_user_group" name="selected_user_group">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Select the devices' %}</h3>
</div>
<div class="panel-body" id="id_block_devices">
{% include 'maps/preload_devices_tmpl.html' with all_devices=existing_devs dot_devices_ids=dot_devices_ids %}
</div>
<div class="panel-footer">
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

112
mapapp/templates/maps/dot.html

@ -1,63 +1,103 @@
{% extends 'base.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'mapapp:options' %}">Настройки карты</a></li>
<li><a href="{% url 'mapapp:options' %}">{% trans 'Map settings' %}</a></li>
{% if dot.id %}
<li class="active">{{ dot.title }}</li>
{% url 'mapapp:edit_dot' dot.id as form_url %}
{% else %}
<li class="active">{% trans 'Add new point' %}</li>
{% url 'mapapp:add_dot' as form_url %}
{% endif %}
</ol>
{% include 'message_block.html' %}
<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 }}"/>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Точка топологии</h3>
<h3 class="panel-title">{% trans 'Map point' %}</h3>
</div>
<div class="panel-body">
{% if dot.id %}
<form role="form" action="{% url 'mapapp:edit_dot' dot.id %}" method="post">
{% else %}
<form role="form" action="{% url 'mapapp:add_dot' %}" method="post">
{% endif %}
{% csrf_token %}
{# title input #}
{% bootstrap_icon 'edit' as ic %}
{% bootstrap_field form.title addon_before=ic %}
<div class="form-group">
<label for="id_title">Название точки топологии</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-edit"></span></span>
{{ form.title }}{{ form.title.errors }}
</div>
</div>
{# longitude input #}
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.longitude addon_before=ic %}
<div class="form-group">
<label for="id_longitude">Longitude</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-globe"></span></span>
{{ form.longitude }}{{ form.longitude.errors }}
</div>
</div>
{# latitude input #}
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.latitude addon_before=ic %}
<div class="form-group">
<label for="id_latitude">Latitude</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-globe"></span></span>
{{ form.latitude }}{{ form.latitude.errors }}
</div>
</div>
{# attachment input #}
{% bootstrap_field form.attachment %}
<div class="btn-group">
<button type="submit" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-save"></span> Сохранить
</div>
<div class="panel-footer">
<div class="btn-group btn-group-sm">
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
<button type="reset" class="btn btn-sm btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> Сбросить
<button type="reset" class="btn btn-default">
<span class="glyphicon glyphicon-remove-circle"></span> {% trans 'Reset' %}
</button>
</div>
</div>
</div>
</form>
</div>
{% if dot.id %}
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{% trans 'Devices' %}</h3>
</div>
<table class="table">
{% for dev in dot.devices.all %}
<tr>
<td>
{% if dev.user_group %}
<a href="{% url 'devapp:view' dev.user_group.pk dev.pk %}">{{ dev.comment }}</a>
{% else %}
<a href="{% url 'devapp:view' 0 dev.pk %}">{{ dev.comment }}</a>
{% endif %}
</td>
<td>{{ dev.ip_address }}</td>
<td>{{ dev.get_comment_display }}</td>
<td>
{% if dev.user_group %}
<a href="{% url 'abonapp:people_list' dev.user_group.pk %}">
{{ dev.user_group }}
</a>
{% else %}
{% trans 'User group has no attached' %}
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">{% trans 'Devices not found' %}</td>
</tr>
{% endfor %}
</table>
<div class="panel-footer">
{% trans 'Add' as trns %}
{% url 'mapapp:add_dev' dot.pk as url %}
{% bootstrap_button trns button_type="link" icon='plus' size='sm' button_class='btn-success' href=url %}
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}

62
mapapp/templates/maps/index.html

@ -1,62 +0,0 @@
{% extends 'base_no_lmenu.html' %}
{% block main %}
<script src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script>
var markers = {
dev_ok: 'dev_ok.png',
dev_bug: 'dev_bug.png',
dev: 'dev.png',
disabled: 'flag_black.png'
};
google.maps.Map.prototype.onMapDblClick = function (e) {
};
google.maps.Map.prototype.SetStaticMarker = function (wlink, type, x, y) {
var dot = new google.maps.LatLng(x, y);
var m = new google.maps.Marker({icon: '/static/img/gmarkers/' + type, position: dot});
var iw = new google.maps.InfoWindow();
google.maps.event.addListener(m, 'click', function () {
var mp = this.map;
$.get(wlink, function (r) {
iw.setContent(r);
iw.open(mp, m);
});
});
m.setMap(this);
};
function MyISGmap(elId) {
var contain_block = document.getElementById(elId);
this.map = new google.maps.Map(contain_block, {
center: new google.maps.LatLng(45.4489018, 34.7880390),
zoom: 11,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
this.map.set("disableDoubleClickZoom", true);
google.maps.event.addListener(this.map, 'dblclick', this.map.onMapDblClick);
}
function initialize() {
var mm = new MyISGmap("main");
$.getJSON("#{ % url 'maps_get_dots' %}", function (r) {
for (var n in r.dots) {
var el = r.dots[n];
mm.map.SetStaticMarker("/", markers.disabled, el[1], el[2]);
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
{% endblock %}

19
mapapp/templates/maps/map_tooltip.html

@ -1,10 +1,23 @@
{% load i18n %}
<h4>{{ dot.title }}</h4>
<div class="list-group">
{% for dev in devs %}
<a class="list-group-item" href="{% url 'devapp:view' dev.user_group.pk dev.pk %}" target="_blank">
{{ dev.comment }}
<a class="list-group-item" href="{% url 'devapp:view' dev.user_group.pk dev.pk %}" target="_blank" title="{{ dev.mon.plugin_output }}">
{% if dev.mon.current_state == '0' %}
<span class="glyphicon glyphicon-ok-circle text-success"></span>
{% else %}
<span class="glyphicon glyphicon-exclamation-sign text-danger"></span>
{% endif %}
<b>{{ dev.comment }}</b> {{ dev.mon.plugin_output }}
</a>
{% empty %}
<li class="list-group-item">нет привязанных устройств</li>
<li class="list-group-item">{% trans 'Pinned devices not found' %}</li>
{% endfor %}
</div>
{% if dot.attachment %}
<span class="glyphicon glyphicon-file"></span>
<a href="{{ dot.attachment.url }}" class="btn btn-xs btn-dfault" target="_blank">{{ dot.attachment.name }}</a>
{% endif %}

20
mapapp/templates/maps/modal_add_device.html

@ -0,0 +1,20 @@
{% load i18n %}{% load bootstrap3 %}
<form action="{% url 'mapapp:add_dev' dot_id %}" method="post">{% csrf_token %}
<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 device' %}</h4>
</div>
<div class="modal-body">
{% bootstrap_form form %}
</div>
<div class="modal-footer">
<div class="btn-group btn-grup-sm">
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</form>

28
mapapp/templates/maps/modal_add_dot.html

@ -1,20 +1,28 @@
<form action="{% url 'mapapp:modal_add_dot' %}" method="post">{% csrf_token %}
{% load i18n %}{% load bootstrap3 %}
<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 }}"/>
<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>Добавить точку</h4>
<h4 class="modal-title"><span class="glyphicon glyphicon-warning-sign"></span>{% trans 'Add point' %}</h4>
</div>
<div class="modal-body">
<h4>Координаты: {{ coords }}</h4>
<div class="form-group">
<label for="id_title">Название точки</label>
<input type="text" class="form-control" autofocus name="title" id="id_title">
</div>
<input type="hidden" name="coords" value="{{ coords }}">
<h4>{% blocktrans %}Coords: {{ coords }}{% endblocktrans %}</h4>
{% bootstrap_icon 'edit' as ic %}
{% bootstrap_field form.title addon_before=ic %}
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.longitude addon_before=ic %}
{% bootstrap_icon 'globe' as ic %}
{% bootstrap_field form.latitude addon_before=ic %}
{% bootstrap_field form.attachment %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-save"></span> Сохранить
<span class="glyphicon glyphicon-save"></span> {% trans 'Save' %}
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
</div>
</form>

38
mapapp/templates/maps/options.html

@ -1,24 +1,24 @@
{% extends 'base.html' %}
{% load i18n %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li class="active">Настройки карты</li>
<li class="active">{% trans 'Map settings' %}</li>
</ol>
{% include 'message_block.html' %}
<h3>Точки топологии</h3>
{% with can_change_dot=perms.mapapp.change_dot can_delete_dot=perms.mapapp.delete_dot %}
<h3>{% trans 'Map point' %}</h3>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Название</th>
<th>Longitude</th>
<th>latitude</th>
<th width="50">Ред.</th>
<th width="50">Уд.</th>
<th>{% trans 'Title' %}</th>
<th>{% trans 'Longitude' %}</th>
<th>{% trans 'Latitude' %}</th>
{% if can_change_dot or can_delete_dot %}<th width="90">#</th>{% endif %}
</tr>
</thead>
<tbody>
@ -27,33 +27,38 @@
<td>{{ dot.title }}</td>
<td>{{ dot.longitude }}</td>
<td>{{ dot.latitude }}</td>
<td colspan="2" class="btn-group-xs">
{% if can_change_dot or can_delete_dot %}
<td colspan="2" class="btn-group btn-group-xs">
{% if perms.mapapp.change_dot %}
<a href="{% url 'mapapp:edit_dot' dot.id %}"
{% if can_change_dot %}
<a href="{% url 'mapapp:edit_dot' dot.id %}" title="{% trans 'Edit' %}" data-toggle="tooltip"
class="btn btn-primary">
<span class="glyphicon glyphicon-edit"></span>
</a>
{% endif %}
{% if perms.mapapp.delete_dot %}
<a href="{% url 'mapapp:remove_dot' dot.id %}"
{% if can_delete_dot %}
<a href="{% url 'mapapp:remove_dot' dot.id %}" title="{% trans 'Delete' %}" data-toggle="tooltip"
class="btn btn-danger">
<span class="glyphicon glyphicon-remove-circle"></span>
<span class="glyphicon glyphicon-remove"></span>
</a>
{% endif %}
</td>
{% endif %}
</tr>
{% empty %}
<tr>
<td colspan="5">Вы ещё не создавали точки топологии. <a href="{% url 'mapapp:add_dot' %}">Создать</a></td>
<td colspan="{% if can_change_dot or can_delete_dot %}4{% else %}3{% endif %}">
{% trans 'You have not created map points yet' %}
<a href="{% url 'mapapp:add_dot' %}">{% trans 'Create' %}</a>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="5">
<td colspan="{% if can_change_dot or can_delete_dot %}4{% else %}3{% endif %}">
<a href="{% url 'mapapp:add_dot' %}" class="btn btn-sm btn-success">
<span class="glyphicon glyphicon-plus"></span>
</a>
@ -62,6 +67,7 @@
</tfoot>
</table>
</div>
{% endwith %}
{% include 'toolbar_page.html' with pag=dots %}
{% endblock %}

7
mapapp/templates/maps/preload_devices_tmpl.html

@ -0,0 +1,7 @@
{% load i18n %}
{% for dev in all_devices %}<div class="checkbox">
<label>
<input name="dv" type="checkbox"{% if dev.pk in dot_devices_ids %} checked{% endif %} value="{{ dev.pk }}"/> {{ dev.comment }}
</label>
</div>
{% empty %}{% trans 'no devices found' %}{% endfor %}

120
mapapp/templates/maps/ya_index.html

@ -1,4 +1,4 @@
{% extends 'base_no_lmenu.html' %}
{% extends 'base_no_lmenu.html' %}{% load i18n %}
{% block main %}
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU" type="text/javascript"></script>
@ -8,7 +8,7 @@
function placemark_click(e){
var plcmrk = e.get('target');
plcmrk.properties.set('balloonContent', "Получаю инфу..");
plcmrk.properties.set('balloonContent', "{% trans 'Loading..' %}");
var html = $.ajax({
url: "{% url 'mapapp:dot_tooltip' %}",
data: {'d': plcmrk.properties._data.dot_id},
@ -18,23 +18,48 @@
}
function load_dots(r){
for(var n=0; n< r.length; n++){
var dot = new ymaps.Placemark(
[r[n].fields.latitude, r[n].fields.longitude],
{
hintContent: r[n].fields.title,
dot_id: r[n].pk
}
);
for(let e of r){
var iconname='flag_black.png';
if(e.devcount > 0)
iconname='dev_ok.png';
dot_place([e.latitude, e.longitude], {
hintContent: e.title,
dot_id: e.pk
}, iconname);
}
}
function dot_place(pos, opts, icon_name='dev_ok.png'){
var dot = new ymaps.Placemark(pos, opts, {
iconLayout: 'default#image',
iconImageHref: '/static/img/gmarkers/'+icon_name,
iconImageSize: [32, 48],
iconImageOffset: [-16, -48]
});
dot.events.add('click', placemark_click);
myMap.geoObjects.add(dot);
}
function on_success_add_dot(r){
hide_ModalMyContent();
var d = $.parseJSON(r);
if(d.error){
alert(d.error);
return false;
}
dot_place([d.latitude, d.longitude], {
hintContent: d.title,
dot_id: d.pk
});
}
function add_dot(e){
var coords = e.get('coords');
$.get('{% url 'mapapp:modal_add_dot' %}', {'coords': coords.join(',')}, function(r){
show_ModalMyContent(r);
$('.form-ajax').ajform({'on_response': on_success_add_dot});
});
e.preventDefault();
}
@ -42,29 +67,92 @@
function init(){
var win = $(window);
$('#yamap')
.css('width', win.width()-5+'px')
.css('height', win.height()+'px');
myMap = new ymaps.Map("yamap", {
center: [45.449160, 34.735454],
zoom: 12
zoom: 12,
controls: ['zoomControl', 'geolocationControl']
});
myMap.controls.add('typeSelector', {position: {left: 45, top: 10}});
$.getJSON("{% url 'mapapp:get_dots' %}", load_dots);
myMap.events.add('dblclick', add_dot);
$('.mapbtns').on('click', function(){
$.getJSON($(this).attr('data-href'), function(dot_ids){
var it = myMap.geoObjects.getIterator(), obj;
var points = [];
while(obj = it.getNext()){
if(obj.geometry == undefined) break;
if(obj.geometry.getType() == "Point"){
var dot_id = obj.properties.get('dot_id');
var is_dot_contains = dot_ids.includes(dot_id);
obj.options.set('visible', is_dot_contains);
if(is_dot_contains)
points.push(obj);
}
}
var bnds = ymaps.geoQuery(points).getBounds();
if(bnds){
if(points.length > 1)
myMap.setBounds(bnds, {duration: 400, checkZoomRange: true, useMapMargin: true, zoomMargin: 30});
else
myMap.panTo([points[0].geometry.getCoordinates()]);
}
});
$('.mapbtns').removeClass('active');
$(this).addClass('active');
});
}
function showAll(){
var it = myMap.geoObjects.getIterator(), obj;
while(obj = it.getNext()){
if(obj.geometry == undefined) break;
if(obj.geometry.getType() == "Point"){
obj.options.set('visible', true);
}
}
myMap.setBounds(myMap.geoObjects.getBounds(), {duration: 400});
$('.mapbtns').removeClass('active');
}
</script>
<div id="yamap"></div>
<div id="yamap" class="col-sm-12"></div>
<style>
#yamap{
margin-left: -15px;
margin-top: -9px;
padding: 0;
position: fixed;
height: 95vh;
}
#yapanel{
position: absolute;
right: 12px;
top: 5px;
}
.list-group-item{
padding: 3px 15px;
}
</style>
<div class="panel panel-default hidden-sm hidden-xs" id="yapanel">
<div class="panel-heading">
<h4 class="modal-title">{% trans 'Layers' %}</h4>
</div>
<div class="list-group">
{% for grp in abon_groups %}
<button type="button" data-href="{% url 'mapapp:resolve_dots_by_group' grp.pk %}" class="list-group-item mapbtns">{{ grp.title }}</button>
{% endfor %}
</div>
<div class="panel-footer">
<a href="#" onclick="showAll();" class="btn btn-default">
<span class="glyphicon glyphicon-eye-open"></span> {% trans 'Show all' %}
</a>
</div>
</div>
{% endblock %}

6
mapapp/urls.py

@ -10,8 +10,12 @@ urlpatterns = [
url(r'^options/add$', views.dot, name='add_dot'),
url(r'^options/(?P<did>\d+)/edit$', views.dot, name='edit_dot'),
url(r'^options/(?P<did>\d+)/remove$', views.remove, name='remove_dot'),
url(r'^options/(?P<did>\d+)/add_dev$', views.add_dev, name='add_dev'),
url(r'^preload_devices$', views.preload_devices, name='preload_devices'),
url(r'^get_dots$', views.get_dots, name='get_dots'),
url(r'^modal_add_dot$', views.modal_add_dot, name='modal_add_dot'),
url(r'^j_dot_tooltip$', views.dot_tooltip, name='dot_tooltip')
url(r'^j_dot_tooltip$', views.dot_tooltip, name='dot_tooltip'),
url(r'^resolve_dots_by_group(?P<grp_id>\d+)$', views.resolve_dots_by_group, name='resolve_dots_by_group')
]

149
mapapp/views.py

@ -2,26 +2,35 @@ from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.gis.shortcuts import render_to_text
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.core.serializers import serialize
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import render, redirect, get_object_or_404, resolve_url
from django.utils.translation import ugettext_lazy as _
from django.db.models import Count
from .models import Dot
from .forms import DotForm
from mydefs import pag_mn
from mydefs import pag_mn, safe_int
from devapp.models import Device
from guardian.decorators import permission_required_or_403 as permission_required
from guardian.decorators import permission_required
from abonapp.models import AbonGroup
from json import dumps
@login_required
def home(request):
if not request.user.is_superuser:
return redirect('/')
dots = Dot.objects.all()
groups = AbonGroup.objects.all()
return render(request, 'maps/ya_index.html', {
'dots': dots
'dots': dots,
'abon_groups': groups
})
@login_required
def options(request):
if not request.user.is_superuser:
return redirect('/')
dots = Dot.objects.all()
dots = pag_mn(request, dots)
return render(request, 'maps/options.html', {
@ -31,6 +40,8 @@ def options(request):
@login_required
def dot(request, did=0):
if not request.user.is_superuser:
return redirect('/')
try:
if did == 0:
dot = Dot()
@ -42,12 +53,13 @@ def dot(request, did=0):
dot = Dot.objects.get(id=did)
if request.method == 'POST':
frm = DotForm(request.POST, instance=dot)
frm = DotForm(request.POST, request.FILES, instance=dot)
if frm.is_valid():
frm.save()
messages.success(request, 'Точка топологии сохранена')
new_dot = frm.save()
messages.success(request, _('Map point has been saved'))
return redirect('mapapp:edit_dot', new_dot.pk)
else:
messages.error(request, 'ошибки в форме')
messages.error(request, _('fix form errors'))
else:
frm = DotForm(instance=dot)
@ -57,7 +69,7 @@ def dot(request, did=0):
})
except Dot.DoesNotExist:
messages.error(request, 'Эта точка топологии не существует')
messages.error(request, _('Map point does not exist'))
return redirect('mapapp:options')
@ -68,49 +80,128 @@ def remove(request, did):
dot = Dot.objects.get(id=did)
title = dot.title
dot.delete()
messages.success(request, "Точка топологии '%s' успешно удалена" % title)
messages.success(request, _("Map point '%(title)s' has been deleted") % {'title': title})
except Dot.DoesNotExist:
messages.error(request, 'Эта точка топологии не существует')
messages.error(request, _('Map point does not exist'))
return redirect('mapapp:options')
@login_required
def get_dots(request):
dots = Dot.objects.all()
return HttpResponse(serialize('json', dots, ensure_ascii=False), content_type='application/json')
if not request.user.is_superuser:
return HttpResponseForbidden('you have not super user')
dots = Dot.objects.all().annotate(devcount=Count('devices'))
res = [{
'devcount': e.devcount,
'latitude': e.latitude,
'longitude': e.longitude,
'title': e.title,
'pk': e.pk
} for e in dots]
return HttpResponse(dumps(res), content_type='application/json')
@login_required
@permission_required('mapapp.add_dot')
def modal_add_dot(request):
#FIXME: тут надо удостовериться что при отсутствии прав, сообщение об этом корректно отобразится
if not request.user.has_perm('mapapp.add_dot'):
return render_to_text('403_for_modal.html')
if request.method == 'POST':
coords = request.POST.get('coords')
title = request.POST.get('title')
lat, lon = coords.split(',')
print(lat, lon)
Dot.objects.create(
title=title,
latitude=float(lat),
longitude=float(lon)
)
return redirect('mapapp:home')
frm = DotForm(request.POST, request.FILES)
if frm.is_valid():
new_dot = frm.save()
res = {
'latitude': new_dot.latitude,
'longitude': new_dot.longitude,
'title': new_dot.title,
'pk': new_dot.pk
}
else:
res = {
'error': _('fix form errors')
}
return HttpResponse(dumps(res))
else:
coords = request.GET.get('coords')
lat, lon = coords.split(',')
frm = DotForm(initial={'latitude': lat, 'longitude': lon})
return render_to_text('maps/modal_add_dot.html', {
'coords': coords
'coords': coords,
'form': frm
}, request=request)
@login_required
def preload_devices(request):
if not request.user.is_superuser:
return HttpResponseForbidden('you have not super user')
grp = request.GET.get('grp')
dot = request.GET.get('dot')
#user_group = AbonGroup.objects.get(pk=grp)
all_devices = Device.objects.filter(user_group__id=grp)
dot_devices = Device.objects.filter(dot__id=dot)
dot_devices_ids = [dev.pk for dev in dot_devices]
ret = render_to_text('maps/preload_devices_tmpl.html', {
'all_devices': all_devices,
'dot_devices_ids': dot_devices_ids
})
return HttpResponse(ret, content_type='text/html')
@login_required
def dot_tooltip(request):
if not request.user.is_superuser:
return render_to_text('403_for_modal.html')
d = request.GET.get('d')
devs, dot = None, None
try:
dot = Dot.objects.get(id=d)
devs = Device.objects.filter(map_dot=dot)
devs = dot.devices.all()
devs = Device.objects.wrap_monitoring_info(devs)
except Dot.DoesNotExist:
pass
return render_to_text('maps/map_tooltip.html', {
'devs': devs,
'dot': dot
})
@login_required
def add_dev(request, did):
if not request.user.is_superuser:
return redirect('/')
groups = AbonGroup.objects.all()
dot = get_object_or_404(Dot, pk=did)
param_user_group = safe_int(request.GET.get('grp'))
if request.method == 'POST':
selected_devs = request.POST.getlist('dv')
selected_user_group = safe_int(request.POST.get('selected_user_group'))
existing_devs = Device.objects.filter(user_group__id=selected_user_group or param_user_group)
if existing_devs.count() > 0:
dot.devices.remove(*[dev.pk for dev in existing_devs])
dot.devices.add(*selected_devs)
url = resolve_url('mapapp:add_dev', did=dot.pk)
return HttpResponseRedirect("%s?grp=%d" % (url, selected_user_group or param_user_group))
else:
existing_devs = Device.objects.filter(user_group=param_user_group)
return render(request, 'maps/add_device.html', {
'groups': groups,
'dot': dot,
'existing_devs': existing_devs,
'grp': param_user_group,
'dot_devices_ids': [dev.pk for dev in Device.objects.filter(dot=dot)]
})
@login_required
def resolve_dots_by_group(request, grp_id):
if not request.user.is_superuser:
return HttpResponseForbidden('you have not super user')
devs = Device.objects.filter(user_group__id=grp_id)
dots = Dot.objects.filter(devices__in=devs).annotate(devcount=Count('devices')).only('pk')
res = [dot.pk for dot in dots]
return HttpResponse(dumps(res), content_type='application/json')

5
requirements.txt

@ -12,6 +12,9 @@ xmltodict
mysqlclient
easysnmp
rq
pid
django-guardian
pinax-theme-bootstrap
django-bootstrap3
requests
webdavclient

48
static/clientside/ISPlaylist.m3u

@ -1,48 +0,0 @@
#EXTM3U
#EXTINF:-1,ПЕРВЫЙ КАНАЛ
udp://@239.255.1.15:1234
#EXTINF:-1,Россия-1
udp://@239.255.1.16:1234
#EXTINF:-1,МАТЧ ТВ
udp://@239.255.1.17:1234
#EXTINF:-1,НТВ
udp://@239.255.1.18:1234
#EXTINF:-1,ПЯТЫЙ КАНАЛ
udp://@239.255.1.19:1234
#EXTINF:-1,РОССИЯ-КУЛЬТУРА
udp://@239.255.1.20:1234
#EXTINF:-1,Россия-24
udp://@239.255.1.21:1234
#EXTINF:-1,КАРУСЕЛЬ
udp://@239.255.1.22:1234
#EXTINF:-1,ОТР
udp://@239.255.1.23:1234
#EXTINF:-1,ТВ Центр
udp://@239.255.1.24:1234
#EXTINF:-1,РЕН ТВ
udp://@239.255.1.28:1234
#EXTINF:-1,Спас
udp://@239.255.1.29:1234
#EXTINF:-1,СТС
udp://@239.255.1.30:1234
#EXTINF:-1,Домашний
udp://@239.255.1.31:1234
#EXTINF:-1,ТВ3
udp://@239.255.1.32:1234
#EXTINF:-1,Звезда
udp://@239.255.1.34:1234
#EXTINF:-1,Мир-24
udp://@239.255.1.9:1234
#EXTINF:-1,ТНТ
udp://@239.255.1.35:1234
#EXTINF:-1,МУЗ ТВ
udp://@239.255.1.36:1234
#EXTINF:-1,Крым 24
udp://@239.255.1.14:1234
#EXTINF:-1,Перец
udp://@239.255.1.12:1234

22
static/css/custom.css

@ -75,6 +75,7 @@ body {
.main {
margin-top: 10px;
display: table;
}
.table-responsive thead {
@ -246,7 +247,22 @@ button[data-toggle=offcanvas]{
fill-opacity: 0.3;
}
a.navbar-brand {
padding-left: 88px;
}
/*
* Ajax loader
*/
div#loading {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
display: none;
}
div#loading>div.gif {
position: absolute;
left: 49%;
top: 39%;
background-color: white;
border-radius: 13px;
border-style: ridge;
}

BIN
static/img/loading.gif

After

Width: 100  |  Height: 100  |  Size: 6.6 KiB

60
static/js/my.js

@ -2,6 +2,7 @@ function show_ModalMyContent(content){
$('#modContent').html(content);
$('#modFrm').modal();
}
function hide_ModalMyContent(){$('#modFrm').modal('hide');}
function show_Modal(title, content, type_class){
var cnt='<div class="modal-header '+type_class+'">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' +
@ -10,6 +11,7 @@ var cnt='<div class="modal-header '+type_class+'">' +
'<div class="modal-body">'+content+'</div>' +
'<div class="modal-footer"><button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button></div>';
show_ModalMyContent(cnt);
$('#loading').hide();
}
function showErr(errContent) {show_Modal('Ошибка', errContent, 'warning');}
@ -17,7 +19,6 @@ function showSuccess(errContent) {show_Modal('Успешно', errContent, 'succ
function showPrimary(errContent) {show_Modal('Внимание!', errContent, 'primary');}
$(document).ajaxError(function (ev, jqXHR, ajaxSettings, thrownError) {
//loaderShow(false);
showErr(jqXHR.status + ': ' + jqXHR.statusText);
});
@ -116,6 +117,61 @@ $(document).ajaxError(function (ev, jqXHR, ajaxSettings, thrownError) {
})(jQuery);
// Ajax Loader
(function ($){
$.fn.ajloader = function(opt){
var settings = $.extend({
dst_block :'id_block_obj'
}, opt);
var fill_block_fn = function(){
$('#loading').show();
var url = $(this).attr('data-href');
$.get(url, function(r){
$(settings.dst_block).html(r);
$('#loading').hide();
});
};
this.each(function(){
$(this).on('click', fill_block_fn);
});
};
})(jQuery);
// Ajax form
(function ($){
$.fn.ajform = function(opt){
var settings = $.extend({
on_response : on_response_default
}, opt);
var on_response_default = function(r){
alert('You must assign callback function for response');
};
var on_submit = function(e){
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: $(this).attr('action'),
type: 'POST',
data: formData,
async: true,
success: settings.on_response,
cache: false,
contentType: false,
processData: false
});
};
this.each(function(){
$(this).on('submit', on_submit);
});
};
})(jQuery);
$(document).ready(function () {
@ -189,4 +245,6 @@ $(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({container:'body'});
$('.btn_ajloader').ajloader({'dst_block': '#id_block_devices'});
});

20
systemd_units/do_backup.sh

@ -0,0 +1,20 @@
#!/bin/bash
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
cd /var/backups
file="djing`date "+%Y-%m-%d_%H.%M.%S"`.sql.gz"
mysql_passw=MYSQL ROOT PASSWORD
echo show tables | mysql -uroot -p$mysql_passw djingdb | \
grep -v '^flowstat' | grep -v 'traflost' | grep -v '^Tables' | \
xargs mysqldump -R -Q --add-locks -uroot --password=$mysql_passw djingdb $1 | gzip -9 > $file
chmod 400 $file
./webdav_backup.py $file
# удаляем старые
find . -name "djing20??-??-??_??.??.??.sql.gz" -mtime +30 -type f -delete

20
systemd_units/webdav_backup.py

@ -0,0 +1,20 @@
#!/usr/bin/env python3
import webdav.client as wc
from webdav.client import WebDavException
from sys import argv
options = {
'webdav_hostname': "https://webdav.yandex.ru/",
'webdav_login': "YANDEX USERNAME",
'webdav_password': "YANDEX PASSWORD"
}
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:
print(we, type(we))

2
taskapp/templates/taskapp/add_edit_task.html

@ -21,7 +21,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="409600"/>
{% csrf_token %}<input type="hidden" name="MAX_FILE_SIZE" value="{{ FILE_UPLOAD_MAX_MEMORY_SIZE }}"/>
<div class="form-group">
<label for="id_descr">{% trans 'Description' %}</label>

8
templates/403.html

@ -1,8 +1,8 @@
<!DOCTYPE html>
<!DOCTYPE html>{% load i18n %}
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head lang="ru">
<meta charset="UTF-8">
<title>В доступе отказано</title>
<title>{% trans 'Permission denied' %}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
@ -36,8 +36,8 @@
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div align="center"><h1>403 - Нет доступа</h1></div>
<div align="center"><p>У вас нет прав для выполнения действий на этой странице</p></div>
<div align="center"><h1>{% trans '403 - Permission denied' %}</h1></div>
<div align="center"><p>{% trans 'You have no permissions for that page' %}</p></div>
</div>
</div>
</div>

12
templates/403_for_modal.html

@ -0,0 +1,12 @@
{% load i18n %}
<div class="modal-header warning">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-lock"></span>{% trans '403 - Permission denied' %}</h4>
</div>
<div class="modal-body">
<div align="center"><h1>{% trans '403 - Permission denied' %}</h1></div>
<div align="center"><p>{% trans 'You have no permissions for that page' %}</p></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close' %}</button>
</div>

10
templates/all_base.html

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="ru">
<html lang="ru_RU">
<head>
<title>InternetService - Админка</title>
<meta charset="UTF-8">
@ -14,6 +14,12 @@
</head>
<body>
<div id="loading">
<div class="gif">
<img src="/static/img/loading.gif"/>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="modFrm" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
@ -39,8 +45,10 @@
{% url 'acc_app:other_profile' request.user.id as home_url %}
<li{% if home_url == request.path %} class="active"{% endif %}><a href="{{ home_url }}">Главная</a></li>
{% if request.user.is_superuser %}
{% url 'mapapp:home' as map_url %}
<li{% if map_url == request.path %} class="active"{% endif %}><a href="{{ map_url }}" target="_blank">Карта</a></li>
{% endif %}
<!--<li><a href="{ % url 'statistics:home' %}" target="_blank">График траффика</a></li>--->

2
templates/base.html

@ -47,11 +47,13 @@
<span class="glyphicon glyphicon-usd"></span> тарифы
</a></li>
{% if request.user.is_superuser %}
{% url 'mapapp:options' as mapapp_ops %}
<li{% if mapapp_ops in request.path %} class="active"{% endif %}>
<a href="{{ mapapp_ops }}">
<span class="glyphicon glyphicon-map-marker"></span> Настройки карты
</a></li>
{% endif %}
{% url 'msg_app:home' as privmsg_home %}
<li{% if privmsg_home in request.path %} class="active"{% endif %}>

Loading…
Cancel
Save