Browse Source

fix bugs

devel
Dmitry 10 years ago
parent
commit
3ddca3f8b5
  1. 1
      .gitignore
  2. 5
      abonapp/apps.py
  3. 2
      abonapp/forms.py
  4. 26
      abonapp/models.py
  5. 54
      abonapp/views.py
  6. 2
      agent.py
  7. 20
      agent/db.py
  8. 5
      agent/firewall.py
  9. 19
      agent/main.py
  10. 8
      agent/models.py
  11. 18
      agent/sslTransmitter.py
  12. 1
      agent/tariff.py
  13. 4
      devapp/models.py
  14. 3
      djing/settings.py
  15. 3
      djing/settings_example.py
  16. 1
      djing/urls.py
  17. 15
      tariff_app/base_intr.py
  18. 2
      tariff_app/custom_tariffs.py
  19. 2
      taskapp/apps.py
  20. 4
      templates/abonapp/peoples.html
  21. 16
      templates/base.html
  22. 2
      templates/taskapp/add_edit_task.html

1
.gitignore

@ -5,3 +5,4 @@ media/min/*
~*/migrations/000*.py ~*/migrations/000*.py
.idea/ .idea/
djing/settings.py djing/settings.py
*/migrations/000*.py

5
abonapp/apps.py

@ -0,0 +1,5 @@
from django.apps import AppConfig
class AbonappConfig(AppConfig):
name = 'abonapp'

2
abonapp/forms.py

@ -36,7 +36,7 @@ class AbonForm(forms.Form):
) )
is_active = forms.BooleanField( is_active = forms.BooleanField(
required=False, required=False,
widget=forms.Select(attrs={'class': 'form-control', 'id': 'isactive'})
widget=forms.NullBooleanSelect(attrs={'class': 'form-control', 'id': 'isactive'})
) )
group = forms.ModelChoiceField( group = forms.ModelChoiceField(

26
abonapp/models.py

@ -15,12 +15,17 @@ from accounts_app.models import UserProfile
class LogicError(Exception): class LogicError(Exception):
def __init__(self, value):
self.value = value
def __init__(self, value, err_id=None):
self.value = value
if err_id:
self.err_id = err_id
def __unicode__(self): def __unicode__(self):
return repr(self.value) return repr(self.value)
def __str__(self):
return repr(self.value)
class AbonGroup(models.Model): class AbonGroup(models.Model):
title = models.CharField(max_length=127) title = models.CharField(max_length=127)
@ -49,7 +54,8 @@ class AbonLog(models.Model):
class AbonTariffManager(models.Manager): class AbonTariffManager(models.Manager):
def update_priorities(self, abonent):
@staticmethod
def update_priorities(abonent):
abon_tariff_list = AbonTariff.objects.filter(abon=abonent).order_by('tariff_priority') abon_tariff_list = AbonTariff.objects.filter(abon=abonent).order_by('tariff_priority')
# Обновляем приоритеты, чтоб по порядку были # Обновляем приоритеты, чтоб по порядку были
@ -134,7 +140,7 @@ class AbonTariff(models.Model):
next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)[:1] next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)[:1]
if next_tarifs.count() < 1: if next_tarifs.count() < 1:
raise LogicError(u'У абонента нет следующих назначенных услуг')
raise LogicError('У абонента нет следующих назначенных услуг')
# 0й элемент это следующая подключаемая услуга # 0й элемент это следующая подключаемая услуга
next_tarifs[0].time_start = timezone.now() next_tarifs[0].time_start = timezone.now()
@ -177,6 +183,7 @@ class Abon(UserProfile):
_act_tar_cache = None _act_tar_cache = None
# возвращает текущий тариф для абонента
def active_tariff(self): def active_tariff(self):
if self._act_tar_cache: if self._act_tar_cache:
return self._act_tar_cache return self._act_tar_cache
@ -317,6 +324,17 @@ class Abon(UserProfile):
comment = u'Завершение услуги по истечению срока действия' comment = u'Завершение услуги по истечению срока действия'
) )
# есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access()
def is_access(self):
trf = self.active_tariff()
if not trf: return False
ct = trf.get_calc_type()
if ct.manage_access(self):
return True
else:
return False
class InvoiceForPayment(models.Model): class InvoiceForPayment(models.Model):
abon = models.ForeignKey(Abon) abon = models.ForeignKey(Abon)

54
abonapp/views.py

@ -213,6 +213,8 @@ def abonhome(request, gid, uid):
try: try:
if request.method == 'POST': if request.method == 'POST':
# подключение к NAS'у в начале для того чтоб если исключение то ничего не сохранялось и сразу показать ошибку
tc = get_TransmitterClientKlass()()
frm = forms.AbonForm(request.POST) frm = forms.AbonForm(request.POST)
if frm.is_valid(): if frm.is_valid():
cd = frm.cleaned_data cd = frm.cleaned_data
@ -229,19 +231,22 @@ def abonhome(request, gid, uid):
abon.is_active = 1 if cd['is_active'] else 0 abon.is_active = 1 if cd['is_active'] else 0
abon.save() abon.save()
# Если включили то шлём событие от этом
# Если включили абонента то шлём событие от этом
if cd['is_active'] and not abisactive: if cd['is_active'] and not abisactive:
tc = get_TransmitterClientKlass()()
tc.signal_abon_enable(abon)
# смотрим есть-ли доступ у абонента к услуге
is_acc = abon.is_access()
if is_acc:
tc.signal_abon_refresh_info(abon)
else:
tc.signal_abon_close_inet(abon)
# Если выключили
# Если выключили абонента
elif not cd['is_active'] and abisactive: elif not cd['is_active'] and abisactive:
tc = get_TransmitterClientKlass()()
tc.signal_abon_disable(abon) tc.signal_abon_disable(abon)
# Если изменили инфу, важную для NAS то говорим NAS'у перечитать инфу об абоненте # Если изменили инфу, важную для NAS то говорим NAS'у перечитать инфу об абоненте
if abon.ip_address != ip_address: if abon.ip_address != ip_address:
tc = get_TransmitterClientKlass()()
tc.signal_abon_refresh_info(abon) tc.signal_abon_refresh_info(abon)
#return redirect('abonhome_link', gid, uid) #return redirect('abonhome_link', gid, uid)
@ -326,7 +331,7 @@ def buy_tariff(request, gid, uid):
cd = frm.cleaned_data cd = frm.cleaned_data
abon.buy_tariff(cd['tariff'], request.user) abon.buy_tariff(cd['tariff'], request.user)
abon.save() abon.save()
return redirect('abonhome_link', uid=abon.id)
return redirect('abonhome_link', gid=gid, uid=abon.id)
else: else:
warntext = u'Что-то не так при покупке услуги, проверьте и попробуйте ещё' warntext = u'Что-то не так при покупке услуги, проверьте и попробуйте ещё'
else: else:
@ -363,25 +368,37 @@ def chpriority(request, gid, uid):
@login_required @login_required
def complete_service(request, gid, uid, srvid): def complete_service(request, gid, uid, srvid):
abtar = get_object_or_404(models.AbonTariff, id=srvid) abtar = get_object_or_404(models.AbonTariff, id=srvid)
abon_group = get_object_or_404(models.AbonGroup, id=gid)
if abtar.abon.id != int(uid): if abtar.abon.id != int(uid):
return HttpResponse('<h1>uid not equal uid from service</h1>') return HttpResponse('<h1>uid not equal uid from service</h1>')
try: try:
if request.method == 'POST': if request.method == 'POST':
tc = get_TransmitterClientKlass()()
abon = abtar.abon
# досрочно завершаем услугу # досрочно завершаем услугу
try: try:
# пробуем активировать следующую услугу
abtar.finish_and_activate_next_tariff(request.user) abtar.finish_and_activate_next_tariff(request.user)
# завершаем текущую услугу.
abtar.delete()
except models.LogicError: except models.LogicError:
# Значит у абонента нет следующих услуг. Сигналим о закрытии инета в NAS
tc = get_TransmitterClientKlass()()
tc.signal_abon_close_inet(abtar.abon)
# Значит у абонента нет следующих услуг. Игнорим, далее в tariff.manage_access() всё разрулится
pass
# завершаем текущую услугу.
abtar.delete()
# Переупорядочиваем приоритеты # Переупорядочиваем приоритеты
models.AbonTariff.objects.update_priorities(abtar.abon)
models.AbonTariff.objects.update_priorities(abon)
# проверяем, может-ли абонент пользоваться новым тарифным планом
if abon.is_access():
# обновляем инфу об абоненте, чтоб применился новый тариф
tc.signal_abon_refresh_info(abon)
else:
# если доступа нет - закрываем инет
tc.signal_abon_close_inet(abon)
return redirect('abonhome_link', gid, uid) return redirect('abonhome_link', gid, uid)
@ -401,12 +418,11 @@ def complete_service(request, gid, uid, srvid):
'minutes': time_use.seconds / 60 % 60 'minutes': time_use.seconds / 60 % 60
} }
return render(request, 'abonapp/complete_service.html', { return render(request, 'abonapp/complete_service.html', {
'csrf_token': csrf(request)['csrf_token'],
'abtar': abtar, 'abtar': abtar,
'abon': get_object_or_404(models.Abon, id=uid),
'abon': abtar.abon,
'next_tariff': next_tariff[0] if next_tariff.count() > 0 else None, 'next_tariff': next_tariff[0] if next_tariff.count() > 0 else None,
'time_use': time_use, 'time_use': time_use,
'abon_group': get_object_or_404(models.AbonGroup, id=gid)
'abon_group': abon_group
}) })
except models.LogicError as e: except models.LogicError as e:
@ -416,10 +432,10 @@ def complete_service(request, gid, uid, srvid):
warntext = e.value warntext = e.value
return render(request, 'abonapp/complete_service.html', { return render(request, 'abonapp/complete_service.html', {
'csrf_token': csrf(request)['csrf_token'],
'abtar': abtar, 'abtar': abtar,
'uid': uid,
'warntext': warntext
'abon': abtar.abon,
'warntext': warntext,
'abon_group': abon_group
}) })

2
agent.py

@ -1,4 +1,4 @@
#!/bin/env python
#!/bin/env python2
import os import os
from agent import main from agent import main

20
agent/db.py

@ -1,19 +1,20 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
import requests import requests
from json import loads from json import loads
from requests.exceptions import ConnectionError
from models import deserialize_tariffs, deserialize_abonents from models import deserialize_tariffs, deserialize_abonents
import settings import settings
def load_from_db(): def load_from_db():
r = requests.get('%s://%s:%d/abons/api/abons' % (
'https' if settings.IS_USE_SSL else 'http',
settings.SERVER_IP,
settings.SERVER_PORT
), verify=False)
try: try:
r = requests.get('%s://%s:%d/abons/api/abons' % (
'https' if settings.IS_USE_SSL else 'http',
settings.SERVER_IP,
settings.SERVER_PORT
), verify=False)
user_data = loads(r.text) user_data = loads(r.text)
del r
# Получаем тарифы # Получаем тарифы
tariffs = deserialize_tariffs(user_data) tariffs = deserialize_tariffs(user_data)
@ -23,5 +24,8 @@ def load_from_db():
return abons, tariffs return abons, tariffs
except ValueError as e: except ValueError as e:
print 'Error:', e, r.text
return
print('Error:', e, r.text)
except ConnectionError:
print("Can not connect to server %s:%d..." % (settings.SERVER_IP, settings.SERVER_PORT))
exit(0)

5
agent/firewall.py

@ -1,4 +1,5 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
from agent.models import Abonent, Tariff
class FirewallManager(object): class FirewallManager(object):
@ -16,6 +17,7 @@ class FirewallManager(object):
# Открывает доступ в интернет # Открывает доступ в интернет
def open_inet_door(self, user): def open_inet_door(self, user):
assert isinstance(user, Abonent)
if not user.tariff: if not user.tariff:
print u'WARNING: User does not have a tariff' print u'WARNING: User does not have a tariff'
return return
@ -27,6 +29,7 @@ class FirewallManager(object):
# Закрывает доступ в интернет # Закрывает доступ в интернет
def close_inet_door(self, user): def close_inet_door(self, user):
assert isinstance(user, Abonent)
cmd = r"%s table 12 del %s/32 && %s table 13 del %s/32" % ( cmd = r"%s table 12 del %s/32 && %s table 13 del %s/32" % (
self.f, user.ip_str(), self.f, user.ip_str(),
self.f, user.ip_str() self.f, user.ip_str()
@ -35,11 +38,13 @@ class FirewallManager(object):
# Создаёт тариф (пайпы, режущие скорость # Создаёт тариф (пайпы, режущие скорость
def make_tariff(self, tariff): def make_tariff(self, tariff):
assert isinstance(tariff, Tariff)
cmd = r"make ipfw tariff :)" cmd = r"make ipfw tariff :)"
self.exec_cmd(cmd) self.exec_cmd(cmd)
# Убирает тариф из фаервола # Убирает тариф из фаервола
def destroy_tariff(self, tariff): def destroy_tariff(self, tariff):
assert isinstance(tariff, Tariff)
cmd = r"destroy ipfw tariff :)" cmd = r"destroy ipfw tariff :)"
self.exec_cmd(cmd) self.exec_cmd(cmd)

19
agent/main.py

@ -4,12 +4,14 @@ from db import load_from_db
from firewall import FirewallManager from firewall import FirewallManager
from time import sleep from time import sleep
from sslTransmitter import TransmitServer from sslTransmitter import TransmitServer
from agent.models import Abonent, Tariff
def filter_user_by_id(users, uid): def filter_user_by_id(users, uid):
usrs = filter(lambda usr: usr.uid == uid, users)
if len(usrs) > 0:
return usrs[0]
users = filter(lambda usr: isinstance(usr, Abonent), users)
users = filter(lambda usr: usr.uid == uid, users)
if len(users) > 0:
return users[0]
else: else:
return return
@ -21,7 +23,7 @@ def main(debug=False):
# Инициализация абонентов # Инициализация абонентов
if debug: if debug:
print u'Инициализация...'
print("Инициализация...")
# Открываем доступ в инет тем кто активен и у кого подключён тариф # Открываем доступ в инет тем кто активен и у кого подключён тариф
for usr in filter(lambda usr: usr.is_active, users): for usr in filter(lambda usr: usr.is_active, users):
@ -30,13 +32,14 @@ def main(debug=False):
if usr.tariff: if usr.tariff:
# Открываем доступ в инет # Открываем доступ в инет
frw.open_inet_door(usr) frw.open_inet_door(usr)
if debug: print "Разрешён доступ в инет для:", usr.ip_str()
# Слушем в отдельном процессе сеть на предмет событий # Слушем в отдельном процессе сеть на предмет событий
ts = TransmitServer('127.0.0.1', 2134) ts = TransmitServer('127.0.0.1', 2134)
ts.start() ts.start()
if debug: if debug:
print u"Загружено %d абонентов" % len(users)
print("Загружено %d абонентов" % len(users))
while True: while True:
@ -44,9 +47,7 @@ def main(debug=False):
events = ts.get_data() events = ts.get_data()
# Проходим по появившимся событиям # Проходим по появившимся событиям
for event in events: for event in events:
#event.toa
#event.id
#event.dt
#event.toa, event.id, event.dt
# Смотрим тип события # Смотрим тип события
toa = int(event.toa) toa = int(event.toa)
@ -108,7 +109,7 @@ def main(debug=False):
frw.destroy_tariff(tariff) frw.destroy_tariff(tariff)
frw.make_tariff(tariff) frw.make_tariff(tariff)
else: else:
print 'WARNING: не найден тариф для которого сигнал на изменение данных, пробуем перезагрузиться'
print('WARNING: не найден тариф для которого возбуждён сигнал на изменение данных, пробуем перезагрузиться')
return return
# Очищаем очередь событий # Очищаем очередь событий

8
agent/models.py

@ -81,12 +81,14 @@ class Abonent(Serializer):
is_active = True is_active = True
def __init__(self, uid=None, ip=None, tariff=None): def __init__(self, uid=None, ip=None, tariff=None):
# none потому что может инициализироваться пустым, чтоб быть распакованным через deserialize()
if tariff:
assert isinstance(tariff, Tariff)
self.ip = ip self.ip = ip
self.uid = uid self.uid = uid
self.tariff = tariff self.tariff = tariff
def ip_str(self): def ip_str(self):
# int2ip, Example out '127.0.0.1'
return socket.inet_ntoa(struct.pack("!I", self.ip)) return socket.inet_ntoa(struct.pack("!I", self.ip))
def _serializable_obj(self): def _serializable_obj(self):
@ -98,6 +100,10 @@ class Abonent(Serializer):
} }
def deserialize(self, dump, tariffs): def deserialize(self, dump, tariffs):
# фильтруем только элементы нужного типа
tariffs = filter(lambda trf: isinstance(trf, Tariff), tariffs)
assert len(tariffs) > 0
inf = loads(dump) if type(dump) == str else dump inf = loads(dump) if type(dump) == str else dump
self.uid = int(inf['id']) self.uid = int(inf['id'])
self.is_active = bool(inf['is_active']) self.is_active = bool(inf['is_active'])

18
agent/sslTransmitter.py

@ -67,10 +67,12 @@ def agent_abon_typer(fn):
if isinstance(abon, Abonent): if isinstance(abon, Abonent):
fn(self, abon) fn(self, abon)
else: else:
act_tar = abon.active_tariff()
agent_tariff = Tariff(act_tar.id, act_tar.speedIn, act_tar.speedOut) if act_tar else None
abn = Abonent( abn = Abonent(
abon.id, abon.id,
abon.ip_address.int_ip() if abon.ip_address else 0, abon.ip_address.int_ip() if abon.ip_address else 0,
abon.active_tariff()
agent_tariff
) )
fn(self, abn) fn(self, abn)
return wrapped return wrapped
@ -108,7 +110,10 @@ class SSLTransmitterClient(object):
port or settings.SELF_PORT port or settings.SELF_PORT
)) ))
except socket.error: except socket.error:
raise NetExcept(u'Ошибка подключения к NAS агенту')
raise NetExcept('Ошибка подключения к NAS агенту %s:%d' % (
ip or settings.SELF_IP,
port or settings.SELF_PORT
))
def write(self, d): def write(self, d):
self.s.write(d) self.s.write(d)
@ -162,7 +167,8 @@ class SSLTransmitterClient(object):
) )
def __del__(self): def __del__(self):
self.s.close()
if self.s:
self.s.close()
class PlainTransmitterClient(SSLTransmitterClient): class PlainTransmitterClient(SSLTransmitterClient):
@ -176,12 +182,16 @@ class PlainTransmitterClient(SSLTransmitterClient):
)) ))
self.s = s self.s = s
except socket.error: except socket.error:
raise NetExcept(u'Ошибка подключения к NAS агенту')
raise NetExcept('Ошибка подключения к NAS агенту на %s:%d' % (
ip or settings.SELF_IP,
port or settings.SELF_PORT
))
def write(self, d): def write(self, d):
self.s.send(d) self.s.send(d)
# общалка с NAS'ом
def get_TransmitterClientKlass(): def get_TransmitterClientKlass():
if settings.IS_USE_SSL: if settings.IS_USE_SSL:
return SSLTransmitterClient return SSLTransmitterClient

1
agent/tariff.py

@ -1 +0,0 @@
# -*- coding:utf-8 -*-

4
devapp/models.py

@ -18,6 +18,9 @@ class Device(models.Model):
man_passw = models.CharField(max_length=16, null=True, blank=True) man_passw = models.CharField(max_length=16, null=True, blank=True)
#map_dot = models.ForeignKey() #map_dot = models.ForeignKey()
class Meta:
db_table = 'dev'
def get_abons(self): def get_abons(self):
pass pass
@ -44,6 +47,7 @@ class Port(models.Model):
speed = models.CharField(max_length=1, default=PORT_SPEEDS[0][0], choices=PORT_SPEEDS) speed = models.CharField(max_length=1, default=PORT_SPEEDS[0][0], choices=PORT_SPEEDS)
class Meta: class Meta:
db_table = 'dev_port'
unique_together = (('device', 'num')) unique_together = (('device', 'num'))

3
djing/settings.py

@ -35,7 +35,8 @@ INSTALLED_APPS = [
'searchapp', 'searchapp',
'devapp', 'devapp',
'mapapp', 'mapapp',
'statistics'
'statistics',
'taskapp'
] ]
MIDDLEWARE_CLASSES = [ MIDDLEWARE_CLASSES = [

3
djing/settings_example.py

@ -35,7 +35,8 @@ INSTALLED_APPS = [
'searchapp', 'searchapp',
'devapp', 'devapp',
'mapapp', 'mapapp',
'statistics'
'statistics',
'taskapp'
] ]
MIDDLEWARE_CLASSES = [ MIDDLEWARE_CLASSES = [

1
djing/urls.py

@ -14,6 +14,7 @@ urlpatterns = [
url(r'^dev/', include('devapp.urls')), url(r'^dev/', include('devapp.urls')),
url(r'^map/', include('mapapp.urls')), url(r'^map/', include('mapapp.urls')),
url(r'^statistic/', include('statistics.urls')), url(r'^statistic/', include('statistics.urls')),
url(r'^tasks/', include('taskapp.urls')),
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
] ]

15
tariff_app/base_intr.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
#from abonapp import Abon
class TariffBase(object):
class TariffBase:
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
# Принимает на вход: # Принимает на вход:
@ -14,3 +15,15 @@ class TariffBase(object):
@staticmethod @staticmethod
def description(): def description():
"""Возвращает текстовое описание""" """Возвращает текстовое описание"""
@staticmethod
def manage_access(abon):
"""Управляет доступом абонента к услуге"""
#assert isinstance(abon, Abon)
# если абонент не активен то выходим
if not abon.is_active: return False
# смотрим на текущую услугу
act_tar = abon.active_tariff()
# если есть услуга
if act_tar:
return True

2
tariff_app/custom_tariffs.py

@ -2,12 +2,14 @@
from django.utils import timezone from django.utils import timezone
from datetime import datetime, timedelta from datetime import datetime, timedelta
from base_intr import TariffBase from base_intr import TariffBase
#from abonapp import AbonTariff
class TariffDefault(TariffBase): class TariffDefault(TariffBase):
# Базовый функционал считает стоимость пропорционально использованному времени # Базовый функционал считает стоимость пропорционально использованному времени
def calc_amount(self, abon_tariff): def calc_amount(self, abon_tariff):
#assert isinstance(abon_tariff, AbonTariff)
# сейчас # сейчас
nw = datetime.now(tz=timezone.get_current_timezone()) nw = datetime.now(tz=timezone.get_current_timezone())

2
taskapp/apps.py

@ -1,5 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
class TicketsappConfig(AppConfig):
class TaskappConfig(AppConfig):
name = 'taskapp' name = 'taskapp'

4
templates/abonapp/peoples.html

@ -46,7 +46,9 @@
</thead> </thead>
<tbody> <tbody>
{% for human in peoples %} {% for human in peoples %}
<tr>
{% if human.is_active %}<tr>
{% else %}<tr class="danger">
{% endif %}
<td>{{ human.id }}</td> <td>{{ human.id }}</td>
<td><a href="{% url 'abonhome_link' human.group.id human.id %}">{{ human.username }}</a></td> <td><a href="{% url 'abonhome_link' human.group.id human.id %}">{{ human.username }}</a></td>
<td>{{ human.ip_address|default:'Не назначен' }}</td> <td>{{ human.ip_address|default:'Не назначен' }}</td>

16
templates/base.html

@ -21,10 +21,10 @@
<div class="modal-header warning"> <div class="modal-header warning">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel"><span class="glyphicon glyphicon-warning-sign"></span> <h4 class="modal-title" id="myModalLabel"><span class="glyphicon glyphicon-warning-sign"></span>
Ошибка</h4>
Надпись вверху</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
...
тут содержимое блока
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button> <button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
@ -151,11 +151,23 @@
<a href="{{ pool_home_link }}"> <a href="{{ pool_home_link }}">
<span class="glyphicon glyphicon-compressed"></span> ip пул <span class="glyphicon glyphicon-compressed"></span> ip пул
</a></li> </a></li>
<li><a href="{% url 'devs_link' %}"> <li><a href="{% url 'devs_link' %}">
<span class="glyphicon glyphicon-hdd"></span> железки <span class="glyphicon glyphicon-hdd"></span> железки
</a></li> </a></li>
{% url 'task_home' as task_home %}
{% if task_home in request.path %}
<li class="active">
{% else %}
<li>
{% endif %}
<a href="{{ task_home }}">
<span class="glyphicon glyphicon-tasks"></span> Задачи
</a></li>
</ul> </ul>
<ul class="nav nav-sidebar"> <ul class="nav nav-sidebar">

2
templates/taskapp/add_edit_task.html

@ -4,7 +4,7 @@
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li> <li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'task_home' %}">Задачи</a></li> <li><a href="{% url 'task_home' %}">Задачи</a></li>
<li class="active">Создать</li>
<li class="active">{% if task.id %}Редактировать{% else %}Создать{% endif %}</li>
</ol> </ol>

Loading…
Cancel
Save