Browse Source

initial commit

devel
Dmitry 10 years ago
commit
2e888db44d
  1. 6
      .gitignore
  2. 65
      Doc.txt
  3. 0
      abonapp/__init__.py
  4. 8
      abonapp/admin.py
  5. 68
      abonapp/forms.py
  6. 110
      abonapp/migrations/0001_initial.py
  7. 0
      abonapp/migrations/__init__.py
  8. 349
      abonapp/models.py
  9. 50
      abonapp/tests.py
  10. 21
      abonapp/urls.py
  11. 19
      abonapp/urls_abon.py
  12. 439
      abonapp/views.py
  13. 0
      accounts_app/__init__.py
  14. 4
      accounts_app/admin.py
  15. 42
      accounts_app/migrations/0001_initial.py
  16. 0
      accounts_app/migrations/__init__.py
  17. 107
      accounts_app/models.py
  18. 16
      accounts_app/tests.py
  19. 27
      accounts_app/urls.py
  20. 258
      accounts_app/views.py
  21. 12
      agent.py
  22. 3
      agent/__init__.py
  23. 27
      agent/db.py
  24. 48
      agent/firewall.py
  25. 72
      agent/ipfw.sh
  26. 120
      agent/main.py
  27. 143
      agent/models.py
  28. 0
      agent/netflow/__init__.py
  29. 49
      agent/netflow/netflow_handler.py
  30. 11
      agent/netflow/netflow_handler.sh
  31. 7
      agent/netflow/start_netflow.sh
  32. 17
      agent/settings.py
  33. 226
      agent/sslTransmitter.py
  34. 1
      agent/tariff.py
  35. 11
      bugs.txt
  36. 18
      cron.py
  37. 0
      devapp/__init__.py
  38. 6
      devapp/admin.py
  39. 7
      devapp/apps.py
  40. 109
      devapp/base_intr.py
  41. 30
      devapp/dev_types.py
  42. 32
      devapp/forms.py
  43. 49
      devapp/migrations/0001_initial.py
  44. 0
      devapp/migrations/__init__.py
  45. 52
      devapp/models.py
  46. 3
      devapp/tests.py
  47. 10
      devapp/urls.py
  48. 59
      devapp/views.py
  49. 0
      djing/__init__.py
  50. 143
      djing/settings.py
  51. 26
      djing/urls.py
  52. 9
      djing/views.py
  53. 16
      djing/wsgi.py
  54. 17
      install.sql
  55. 1
      ip_pool/__init__.py
  56. 4
      ip_pool/admin.py
  57. 6
      ip_pool/apps.py
  58. 21
      ip_pool/forms.py
  59. 24
      ip_pool/migrations/0001_initial.py
  60. 0
      ip_pool/migrations/__init__.py
  61. 60
      ip_pool/models.py
  62. 3
      ip_pool/tests.py
  63. 13
      ip_pool/urls.py
  64. 76
      ip_pool/views.py
  65. 10
      manage.py
  66. 0
      mapapp/__init__.py
  67. 4
      mapapp/admin.py
  68. 7
      mapapp/apps.py
  69. 25
      mapapp/migrations/0001_initial.py
  70. 0
      mapapp/migrations/__init__.py
  71. 10
      mapapp/models.py
  72. 3
      mapapp/tests.py
  73. 7
      mapapp/urls.py
  74. 18
      mapapp/views.py
  75. 111
      mydefs.py
  76. 0
      photo_app/__init__.py
  77. 4
      photo_app/admin.py
  78. 25
      photo_app/migrations/0001_initial.py
  79. 0
      photo_app/migrations/__init__.py
  80. 69
      photo_app/models.py
  81. 16
      photo_app/tests.py
  82. 12
      photo_app/urls.py
  83. 1
      photo_app/views.py
  84. 0
      privatemessage/__init__.py
  85. 5
      privatemessage/admin.py
  86. 7
      privatemessage/context_processors.py
  87. 39
      privatemessage/migrations/0001_initial.py
  88. 0
      privatemessage/migrations/__init__.py
  89. 34
      privatemessage/models.py
  90. 22
      privatemessage/tests.py
  91. 8
      privatemessage/urls.py
  92. 54
      privatemessage/views.py
  93. 4
      requirements.txt
  94. 0
      searchapp/__init__.py
  95. 3
      searchapp/admin.py
  96. 7
      searchapp/apps.py
  97. 0
      searchapp/migrations/__init__.py
  98. 5
      searchapp/models.py
  99. 3
      searchapp/tests.py
  100. 7
      searchapp/urls.py

6
.gitignore

@ -0,0 +1,6 @@
*.pyc
*.db
media/*
media/min/*
~*/migrations/000*.py
.idea/

65
Doc.txt

@ -0,0 +1,65 @@
// Формат общения NAS с базой
[
{
"toa": 1, /* Тип события:
0 - ничего не надо (пустое)
1 - Активировать клиента (включить его)
2 - Выключить клиента
3 - Поставить заглушку
4 - Открыть доступ в интернет
5 - Закрыть доступ в интернет
6 - Перечитать всю инфу (полная перезагрузка NAS)
7 - Изменилась инфа об абоненте, переприменить его
*/
"id": 12, // ID объекта о котором событие (абонент там, или тариф)
"dt": "data" // Разная инфа, содержимое зависит от поля 'toa'
},
{
"id": 13,
"toa": 3
}
]
// Формат передачи инфы об абонентах
{
"subscribers": [
{
"is_active": true, // Активен-ли абонент
"ip": 168558850, // Его ip
"tarif_id": 1, // id тарифа
"id": 2 // id абонента
},
{
"is_active": true,
"ip": 168558850,
"tarif_id": 1,
"id": 2
}
],
"tariffs": [
{
"tid": 1, // id тарифа
"amount": 0.0, // стоимость
"speedOut": 0.0, // Исходящая скорость
"speedIn": 0.0 // Входящая скорость
}
]
}
-------------------------------
Состояние оплаты абонента определяется на основе присутствия у него тарифного плана,
если он есть - то значит всё оплачено (абонент его купил) и может пользоваться услугами.
Тарифный план имеет срок действия и стоимость. Его можно купить, как билет в интернет :)
ТАРИФНЫЙ ПЛАН С НАИМЕНЬШИМ ЧИСЛОМ ПРИОРИТЕТА ИМЕЕТ НАИВЫСШИЙ ПРИОРИТЕТ
В общем чем ближе приоритет к 0 тем он выше
0 - текущий тариф
Свою логику расчёта по тарифу можно добавить в файле tariff_app/custom_tariffs.py
Там надо добавить класс, наследованный от TariffBase и реализовать его абстрактные методы,
потом добавить этот класс в кортеж TARIFF_CHOICES указав:
код из 2х букв, сочетание должно быть уникальным
и ваш класс для своей логики расчёта тарифа

0
abonapp/__init__.py

8
abonapp/admin.py

@ -0,0 +1,8 @@
from django.contrib import admin
import models
admin.site.register(models.AbonGroup)
admin.site.register(models.Abon)
admin.site.register(models.InvoiceForPayment)
admin.site.register(models.AbonLog)
admin.site.register(models.AbonTariff)

68
abonapp/forms.py

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
from django import forms
from django.core.validators import RegexValidator
import models
from mydefs import ip_addr_regex
class AbonForm(forms.Form):
username = forms.CharField(max_length=127, required=False, widget=forms.TextInput(attrs={
'placeholder': u'Логин',
'class': "form-control",
'id': "login"
}))
fio = forms.CharField(max_length=256, widget=forms.TextInput(attrs={
'placeholder': u'ФИО',
'class': "form-control",
'id': "fio"
}), required=False)
ip_address = forms.GenericIPAddressField(protocol='IPv4', required=False, widget=forms.TextInput(attrs={
'pattern': ip_addr_regex,
'placeholder': u'127.0.0.1',
'class': "form-control",
'id': "ip"
}))
telephone = forms.CharField(
max_length=16,
validators=[RegexValidator(r'^\+[7,8,9,3]\d{10,11}$')],
widget=forms.TextInput(attrs={
'placeholder': u'+[7,8,9,3] и 10,11 цифр',
'pattern': r'^\+[7,8,9,3]\d{10,11}$',
'required': '',
'class': 'form-control',
'id': 'telephone'
})
)
is_active = forms.BooleanField(
required=False,
widget=forms.Select(attrs={'class': 'form-control', 'id': 'isactive'})
)
group = forms.ModelChoiceField(
queryset=models.AbonGroup.objects.all(),
required=False,
widget=forms.Select(attrs={'class': 'form-control', 'id': 'grp'})
)
address = forms.CharField(
max_length=256,
required=False,
widget = forms.TextInput(attrs={'class': 'form-control', 'id': 'address'})
)
class AbonGroupForm(forms.ModelForm):
class Meta:
model = models.AbonGroup
fields = ['title', 'address']
widgets = {
'class': 'form-control'
}
class BuyTariff(forms.Form):
tariff = forms.ModelChoiceField(
queryset=models.Tariff.objects.all(),
required=True,
widget=forms.Select(attrs={'class': 'form-control', 'id': 'tariff'})
)

110
abonapp/migrations/0001_initial.py

@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('accounts_app', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('ip_pool', '0001_initial'),
('tariff_app', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Abon',
fields=[
('userprofile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
('ballance', models.FloatField(default=0.0, validators=[django.core.validators.DecimalValidator])),
('address', models.CharField(max_length=256)),
],
options={
'db_table': 'abonent',
},
bases=('accounts_app.userprofile',),
),
migrations.CreateModel(
name='AbonGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=127)),
('address', models.CharField(blank=True, max_length=256, null=True)),
],
options={
'db_table': 'abonent_groups',
},
),
migrations.CreateModel(
name='AbonLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.FloatField(default=0.0)),
('comment', models.CharField(max_length=128)),
('date', models.DateTimeField(auto_now_add=True)),
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'abonent_log',
},
),
migrations.CreateModel(
name='AbonTariff',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('tariff_priority', models.PositiveSmallIntegerField(default=0)),
('time_start', models.DateTimeField(blank=True, default=None, null=True)),
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('tariff', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='linkto_tariff', to='tariff_app.Tariff')),
],
options={
'ordering': ('tariff_priority',),
'db_table': 'abonent_tariff',
},
),
migrations.CreateModel(
name='InvoiceForPayment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.BooleanField(default=False)),
('amount', models.FloatField(default=0.0)),
('comment', models.CharField(max_length=128)),
('date_create', models.DateTimeField(auto_now_add=True)),
('date_pay', models.DateTimeField(blank=True, null=True)),
('abon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abonapp.Abon')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('date_create',),
'db_table': 'abonent_inv_pay',
},
),
migrations.AddField(
model_name='abon',
name='current_tariffs',
field=models.ManyToManyField(through='abonapp.AbonTariff', to='tariff_app.Tariff'),
),
migrations.AddField(
model_name='abon',
name='group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='abonapp.AbonGroup'),
),
migrations.AddField(
model_name='abon',
name='ip_address',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ip_pool.IpPoolItem'),
),
migrations.AlterUniqueTogether(
name='abontariff',
unique_together=set([('abon', 'tariff', 'tariff_priority')]),
),
]

0
abonapp/migrations/__init__.py

349
abonapp/models.py

@ -0,0 +1,349 @@
# -*- coding: utf-8 -*-
from django.core.exceptions import MultipleObjectsReturned
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.datetime_safe import datetime
from agent import get_TransmitterClientKlass, Abonent, Tariff as AgentTariff
from ip_pool.models import IpPoolItem
from tariff_app.models import Tariff
from django.db import models
from djing import settings
from django.core.validators import DecimalValidator
from accounts_app.models import UserProfile
class LogicError(Exception):
def __init__(self, value):
self.value = value
def __unicode__(self):
return repr(self.value)
class AbonGroup(models.Model):
title = models.CharField(max_length=127)
address = models.CharField(max_length=256, blank=True, null=True)
class Meta:
db_table = 'abonent_groups'
def __unicode__(self):
return self.title
class AbonLog(models.Model):
abon = models.ForeignKey('Abon')
amount = models.FloatField(default=0.0)
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
comment = models.CharField(max_length=128)
date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'abonent_log'
def __unicode__(self):
return self.comment
class AbonTariffManager(models.Manager):
def update_priorities(self, abonent):
abon_tariff_list = AbonTariff.objects.filter(abon=abonent).order_by('tariff_priority')
# Обновляем приоритеты, чтоб по порядку были
at_pr = 0
for at in abon_tariff_list:
at.tariff_priority = at_pr
at_pr += 1
at.save()
class AbonTariff(models.Model):
abon = models.ForeignKey('Abon')
tariff = models.ForeignKey(Tariff, related_name='linkto_tariff')
tariff_priority = models.PositiveSmallIntegerField(default=0)
# время начала действия, остальные что не начали действие - NULL
time_start = models.DateTimeField(null=True, blank=True, default=None)
objects = AbonTariffManager()
def priority_up(self):
# ищем услугу с большим приоритетом(число приоритета меньше)
target_abtar = AbonTariff.objects.filter(
abon=self.abon,
tariff_priority__lt=self.tariff_priority
).order_by('-tariff_priority')[:1]
if target_abtar.count() > 0:
target_abtar = target_abtar[0]
else:
return
# Ищем текущий тариф абонента
active_abtar = AbonTariff.objects.filter(
abon=self.abon
)[:1]
if active_abtar.count() > 0:
active_abtar = active_abtar[0]
else:
return
# Если услуга с которой хотим поменяться приоритетом является текущей то нельзя меняться
if active_abtar == target_abtar:
return
# Swap приоритетов у текущего и найденного с меньшим tariff_priority (большим приоритетом)
tmp_prior = target_abtar.tariff_priority
target_abtar.tariff_priority = self.tariff_priority
target_abtar.save()
self.tariff_priority = tmp_prior
self.save()
def priority_down(self):
# ищем услугу с меньшим приоритетом
target_abtar = AbonTariff.objects.filter(
abon=self.abon,
tariff_priority__gt=self.tariff_priority
)[:1]
if target_abtar.count() > 0:
target_abtar = target_abtar[0]
else:
# меньше нет, это самая последняя услуга
return
# Swap приоритетов у текущего и найденного с большим tariff_priority (меньшим приоритетом)
tmp_pr = self.tariff_priority
self.tariff_priority = target_abtar.tariff_priority
target_abtar.tariff_priority = tmp_pr
target_abtar.save()
self.save()
# Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации)
def calc_amount_service(self):
calc_obj = self.tariff.get_calc_type()
# calc_obj - instance of tariff_app.custom_tariffs.TariffBase
amount = calc_obj.calc_amount(self)
return round(amount, 2)
# досрочно завершает услугу
def finish_and_activate_next_tariff(self, author):
# выберем следующие по приоритету услуги
next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)[:1]
if next_tarifs.count() < 1:
raise LogicError(u'У абонента нет следующих назначенных услуг')
# 0й элемент это следующая подключаемая услуга
next_tarifs[0].time_start = timezone.now()
next_tarifs[0].save()
# сколько денег стоят потраченные ресурсы
used_services = self.calc_amount_service()
#теперь к текущему баллансу добавляем сумму не потраченных ресурсов, т.к. полная сумма тарифа списывается при покупке тарифа
ret_amount = self.tariff.amount - used_services
self.abon.ballance += ret_amount
self.abon.save()
AbonLog.objects.create(
abon = self.abon,
amount = ret_amount,
author = author,
comment = u'Досрочное завершение услуги %s' % (self.tariff.title)
)
def __unicode__(self):
return "%d: '%s' - '%s'" % (
self.tariff_priority,
self.tariff.title,
self.abon.get_short_name()
)
class Meta:
ordering = ('tariff_priority',)
db_table = 'abonent_tariff'
unique_together = (('abon', 'tariff', 'tariff_priority'),)
class Abon(UserProfile):
current_tariffs = models.ManyToManyField(Tariff, through=AbonTariff)
group = models.ForeignKey(AbonGroup, models.SET_NULL, blank=True, null=True)
ballance = models.FloatField(default=0.0, validators=[DecimalValidator])
ip_address = models.OneToOneField(IpPoolItem, on_delete=models.SET_NULL, null=True, blank=True)
address = models.CharField(max_length=256)
_act_tar_cache = None
def active_tariff(self):
if self._act_tar_cache:
return self._act_tar_cache
ats = AbonTariff.objects.filter(abon=self)[:1]
if ats.count() > 0:
self._act_tar_cache = ats[0].tariff
return ats[0].tariff
else:
self._act_tar_cache = None
return
def save_form(self, abonform_instance):
try:
cd = abonform_instance.cleaned_data
tel = cd['telephone']
self.username = cd['username'] or tel[1:]
self.fio = cd['fio']
self.telephone = tel
self.is_admin = False
self.ip_address = get_object_or_404(IpPoolItem, ip=cd['ip_address'])
self.is_active = True
self.group = cd['group']
self.address = cd['address']
except Http404:
raise LogicError(u'Введённый IP адрес не добавлен в ip pool')
except MultipleObjectsReturned:
raise LogicError(u'Введённый IP адрес не определён')
class Meta:
db_table = 'abonent'
def make_pay(self, curuser, how_match_to_pay=0.0):
AbonLog.objects.create(
abon = self,
amount = -how_match_to_pay,
author = curuser,
comment = u'Снятие со счёта средств'
)
self.ballance -= how_match_to_pay
def add_ballance(self, current_user, amount):
AbonLog.objects.create(
abon = self,
amount = amount,
author = current_user,
comment = u'Пополнение счёта через админку'
)
self.ballance += amount
def buy_tariff(self, tariff, author):
if self.ballance >= tariff.amount:
# денег достаточно, можно покупать
self.ballance -= tariff.amount
# выбераем связь ТарифАбонент с самым низким приоритетом
abtrf = AbonTariff.objects.filter(abon=self).order_by('-tariff_priority')[:1]
abtrf = abtrf[0] if abtrf.count() > 0 else None
# создаём новую связь с приоритетом ещё ниже
new_abtar = AbonTariff(
abon=self,
tariff=tariff,
tariff_priority=abtrf.tariff_priority+1 if abtrf else -1
)
# Если это первая услуга в списке (фильтр по приоритету ничего не вернул)
if not abtrf:
# значит она сразу стаёт активной
new_abtar.time_start = timezone.now()
new_abtar.save()
# шлём сигнал о том что абонент купил первую услугу, а значит можно пользоваться инетом
# сигнал можно слать только после того как будет сохранён новый объект AbonTariff
if self.is_active and not abtrf:
tc = get_TransmitterClientKlass()()
act_tar = self.active_tariff()
agent_abon = Abonent(
self.id,
self.ip_address.int_ip(),
AgentTariff(
act_tar.id if act_tar else 0,
act_tar.speedIn if act_tar else 0.0,
act_tar.speedOut if act_tar else 0.0
)
)
tc.signal_abon_refresh_info(agent_abon)
tc.signal_abon_open_inet(agent_abon)
# Запись об этом в лог
AbonLog.objects.create(
abon = self, amount = -tariff.amount,
author = author,
comment = u'Покупка тарифного плана через админку, тариф "%s"' % tariff.title
)
else:
raise LogicError(u'Недостаточно денег на счету абонента')
# Пробует подключить новую услугу если пришло время
def activate_next_tariff(self, author):
ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority')
nw = datetime.now(tz=timezone.get_current_timezone())
for at in ats:
# Если времени активации нет, то это ещё не активированный тариф
if not at.time_start:
return
# время к началу месяца
to_start_month = datetime(nw.year, nw.month, 1, tzinfo=timezone.get_current_timezone())
# проверяем расстояние от Сегодня до начала этого месяца.
# И от заказа тарифа до начала этого месяца
if (nw - at.time_start) > (nw - to_start_month):
# Заказ из прошлого месяца, срок действия закончен
print u'Заказ из прошлого месяца, срок действия закончен'
# выберем следующую по приоритету
#next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)
next_tarifs = filter(lambda tr: tr.tariff_priority > at.tariff_priority, ats)[:2]
# и если что-нибудь вернулось то активируем, давая время начала действия
if next_tarifs.count() > 0:
next_tarifs[0].time_start = nw
next_tarifs[0].save()
# завершаем текущую услугу.
at.delete()
# Создаём лог о завершении услуги
AbonLog.objects.create(
abon = self,
amount = 0,
author = author,
comment = u'Завершение услуги по истечению срока действия'
)
class InvoiceForPayment(models.Model):
abon = models.ForeignKey(Abon)
status = models.BooleanField(default=False)
amount = models.FloatField(default=0.0)
comment = models.CharField(max_length=128)
date_create = models.DateTimeField(auto_now_add=True)
date_pay = models.DateTimeField(blank=True, null=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
def __unicode__(self):
return "%s -> %d $" % (self.abon.username, self.amount)
def set_ok(self):
self.status = True
self.date_pay = datetime.now()
def get_prev_invoice(self):
return self.objects.order
class Meta:
ordering = ('date_create',)
db_table = 'abonent_inv_pay'
#def abon_save_signal(sender, instance, **kwargs):
# if not kwargs['created']:
# # if not create (change only)
# print "Kw1", instance.username, instance.is_active
#models.signals.post_save.connect(abon_save_signal, sender=Abon)

50
abonapp/tests.py

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from django.shortcuts import get_object_or_404
from django.test import TestCase
from models import Abon, AbonTariff
from tariff_app.models import Tariff
class AbonTariffTestCase(TestCase):
def setUp(self):
abon1 = Abon.objects.create(
telephone='+79784653751',
fio=u'ФИО абона',
username='аго мучич'
)
tarif1 = Tariff.objects.create(
title=u'Тариф 1',
speedIn=120.3,
speedOut=53,
amount=38
)
tarif2 = Tariff.objects.create(
title=u'Тариф 2',
speedIn=130.3,
speedOut=23,
amount=82
)
AbonTariff.objects.create(
abon=abon1,
tariff=tarif1,
tariff_priority=0
)
AbonTariff.objects.create(
abon=abon1,
tariff=tarif2,
tariff_priority=1
)
def test_activate_next(self):
# возьмём абонента для опытов
abn = get_object_or_404(Abon, username=u'аго мучич')
# берём купленные услуги
ats = AbonTariff.objects.filter(abon=abn)
for at in ats:
# и пробуем назначить
at.activate_next_tariff()
AbonTariff.objects.update_priorities(ats)

21
abonapp/urls.py

@ -0,0 +1,21 @@
from django.conf.urls import url, include
import views
urlpatterns = [
url(r'^$', views.grouplist, name='abongroup_list_link'),
url(r'^addgroup$', views.addgroup, name='addgroup_link'),
url(r'^delgroup', views.delgroup, name='people_delgroup_link'),
url(r'^(?P<gid>\d+)/', include('abonapp.urls_abon')),
url(r'^log$', views.log_page, name='abonapp_log_link'),
url(r'^del$', views.delentity, name='abonapp_del_link'),
url(r'^pay$', views.terminal_pay, name='abonapp_terminalpay_link'),
# Api's
url(r'^api/abons$', views.abons)
]

19
abonapp/urls_abon.py

@ -0,0 +1,19 @@
from django.conf.urls import url
import views
urlpatterns = [
url(r'^$', views.peoples, name='pays_people_list_link'),
url(r'^addabon$', views.addabon, name='addabon_link'),
url(r'^(?P<uid>\d+)$', views.abonhome, name='abonhome_link'),
url(r'^(?P<uid>\d+)/services$', views.abon_services, name='abon_services_link'),
url(r'^(?P<uid>\d+)/amount', views.abonamount, name='abon_amount_link'),
url(r'^(?P<uid>\d+)/debts', views.invoice_for_payment, name='abon_debts_link'),
url(r'^(?P<uid>\d+)/pay_history', views.pay_history, name='abon_phistory_link'),
url(r'^(?P<uid>\d+)/addinvoice$', views.add_invoice, name='abonapp_addinvoice_link'),
url(r'^(?P<uid>\d+)/buy$', views.buy_tariff, name='abonapp_buy_tariff'),
url(r'^(?P<uid>\d+)/chpriority$', views.chpriority, name='abonapp_chpriority_tariff'),
url(r'^(?P<uid>\d+)/complete_service_(?P<srvid>\d+)$', views.complete_service, name='abonapp_compl_srv'),
]

439
abonapp/views.py

@ -0,0 +1,439 @@
# -*- coding: utf-8 -*-
from json import dumps
from django.db import IntegrityError
from django.db.models import Count
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from accounts_app.models import UserProfile
from ip_pool.models import IpPoolItem
from tariff_app.models import Tariff
from django.template.context_processors import csrf
from django.http import HttpResponse, Http404
from agent import get_TransmitterClientKlass, NetExcept
import forms
import models
import mydefs
@login_required
def peoples(request, gid):
peopleslist = models.Abon.objects.filter(group=gid)
peopleslist = mydefs.pag_mn(request, peopleslist)
return render(request, 'abonapp/peoples.html', {
'peoples': peopleslist,
'gid': gid
})
@login_required
# @permission_required('abonapp.add_abongroup')
def addgroup(request):
warntext = ''
frm = forms.AbonGroupForm()
if request.method == 'POST':
frm = forms.AbonGroupForm(request.POST)
if frm.is_valid():
frm.save()
return redirect('abongroup_list_link')
else:
warntext = u'Исправьте ошибки'
return render(request, 'abonapp/addGroup.html', {
'csrf_token': csrf(request)['csrf_token'],
'form': frm,
'warntext': warntext
})
@login_required
def grouplist(request):
groups = models.AbonGroup.objects.annotate(usercount=Count('abon'))
groups = mydefs.pag_mn(request, groups)
return render(request, 'abonapp/group_list.html', {
'groups': groups
})
@login_required
def delgroup(request):
agd = mydefs.safe_int(request.GET.get('id'))
get_object_or_404(models.AbonGroup, id=agd).delete()
return mydefs.res_success(request, 'abongroup_list_link')
@login_required
# @permission_required('abonapp.add_abon')
# @permission_required('abonapp.change_abon')
def addabon(request, gid):
warntext = ''
frm = None
try:
grp = get_object_or_404(models.AbonGroup, id=gid)
if request.method == 'POST':
frm = forms.AbonForm(request.POST)
if frm.is_valid():
prf = models.Abon()
prf.group = grp
prf.save_form(frm)
prf.save()
return redirect('pays_people_list_link', gid)
else:
warntext = u'Некоторые поля заполнены не правильно, проверте ещё раз'
except IntegrityError, e:
warntext = "%s: %s" % (warntext, e)
except models.LogicError as e:
warntext = e.value
if not frm:
frm = forms.AbonForm(initial={
'ip_address': IpPoolItem.objects.get_free_ip(),
'group': grp
})
return render(request, 'abonapp/addAbon.html', {
'warntext': warntext,
'csrf_token': csrf(request)['csrf_token'],
'form': frm,
'gid': gid
})
@login_required
# @permission_required('abonapp.delete_abon')
# @permission_required('abonapp.delete_abongroup')
def delentity(request):
typ = request.GET.get('t')
uid = request.GET.get('id')
if typ == 'a':
abon = get_object_or_404(models.Abon, id=uid)
abon.delete()
return mydefs.res_success(request, 'pays_people_list_link')
elif typ == 'g':
get_object_or_404(models.AbonGroup, id=uid).delete()
return mydefs.res_success(request, 'pays_people_list_link')
@login_required
def abonamount(request, gid, uid):
warntext=''
if request.method == 'POST':
abonid = mydefs.safe_int(request.POST.get('abonid'))
if abonid == int(uid):
amnt = mydefs.safe_float(request.POST.get('amount'))
abon = get_object_or_404(models.Abon, id=abonid)
abon.add_ballance(request.user, amnt)
abon.save()
return redirect('abonhome_link', gid=gid, uid=uid)
else:
warntext = u'Не правильно выбран абонент как цель для пополнения'
return render(request, 'abonapp/abonamount.html', {
'abon_id': int(uid),
'gid': gid,
'warntext': warntext
})
@login_required
def invoice_for_payment(request, gid, uid):
#abon = get_object_or_404(models.Abon, id=uid)
invoices = models.InvoiceForPayment.objects.filter(abon_id=uid)
invoices = mydefs.pag_mn(request, invoices)
return render(request, 'abonapp/invoiceForPayment.html', {
'invoices': invoices,
'gid': gid,
'abon_id': uid
})
@login_required
def pay_history(request, gid, uid):
#abon = get_object_or_404(models.Abon, id=uid)
pay_history = models.AbonLog.objects.filter(abon_id=uid).order_by('-id')
pay_history = mydefs.pag_mn(request, pay_history)
return render(request, 'abonapp/payHistory.html', {
'pay_history': pay_history,
'gid': gid,
'abon_id': uid
})
@login_required
def abon_services(request, gid, uid):
#abon = get_object_or_404(models.Abon, id=uid)
abon_tarifs = models.AbonTariff.objects.filter(abon_id=uid).order_by('tariff_priority')
return render(request, 'abonapp/services.html', {
'abon_id': uid,
'abon_tarifs': abon_tarifs,
'active_abontariff_id': abon_tarifs[0].id if abon_tarifs.count() > 0 else None,
'gid': gid
})
@login_required
def abonhome(request, gid, uid):
abon = get_object_or_404(models.Abon, id=uid)
warntext = ''
ballance = abon.ballance
frm = None
init_frm_dat = {
'username': abon.username,
'fio': abon.fio,
'ip_address': abon.ip_address or u'Не назначен',
'telephone': abon.telephone,
'group': abon.group,
'address': abon.address,
'is_active': abon.is_active
}
try:
if request.method == 'POST':
frm = forms.AbonForm(request.POST)
if frm.is_valid():
cd = frm.cleaned_data
abon.username = cd['username']
abon.fio = cd['fio']
ip_address = abon.ip_address
abon.ip_address = get_object_or_404(IpPoolItem, ip=cd['ip_address'])
abon.telephone = cd['telephone']
abon.group = cd['group']
abon.address = cd['address']
abisactive = abon.is_active
abon.is_active = 1 if cd['is_active'] else 0
abon.save()
# Если включили то шлём событие от этом
if cd['is_active'] and not abisactive:
tc = get_TransmitterClientKlass()()
tc.signal_abon_enable(abon)
# Если выключили
elif not cd['is_active'] and abisactive:
tc = get_TransmitterClientKlass()()
tc.signal_abon_disable(abon)
# Если изменили инфу, важную для NAS то говорим NAS'у перечитать инфу об абоненте
if abon.ip_address != ip_address:
tc = get_TransmitterClientKlass()()
tc.signal_abon_refresh_info(abon)
#return redirect('abonhome_link', gid, uid)
else:
warntext = u'Не правильные значения, проверте поля и попробуйте ещё'
else:
frm = forms.AbonForm(initial=init_frm_dat)
except IntegrityError, e:
warntext = u'Проверте введённые вами значения, скорее всего такой ip уже у кого-то есть. А вообще: %s' % e
frm = forms.AbonForm(initial=init_frm_dat)
except Http404:
warntext = u'Ip адрес не найден в списке IP адресов'
frm = forms.AbonForm(initial=init_frm_dat)
except NetExcept as e:
warntext = e.value
return render(request, 'abonapp/editAbon.html', {
'warntext': warntext,
'form': frm or forms.AbonForm(initial=init_frm_dat),
'abon_id': uid,
'ballance': ballance,
'gid': gid
})
def terminal_pay(request):
username = request.GET.get('username')
amount = mydefs.safe_float(request.GET.get('amount'))
kernel_user = get_object_or_404(UserProfile, username='kernel')
abon = get_object_or_404(models.Abon, username=username)
abon.add_ballance(kernel_user, amount)
abon.save()
return HttpResponse('ok')
@login_required
# @permission_required('abonapp.add_invoiceforpayment')
def add_invoice(request, gid, uid):
uid = mydefs.safe_int(uid)
abon = get_object_or_404(models.Abon, id=uid)
if request.method == 'POST':
curr_amount = mydefs.safe_int(request.POST.get('curr_amount'))
comment = request.POST.get('comment')
newinv = models.InvoiceForPayment()
newinv.abon = abon
newinv.amount = curr_amount
newinv.comment = comment
if request.POST.get('status') == u'on':
newinv.status = True
newinv.author = request.user
newinv.save()
return redirect('abonhome_link', gid=gid, uid=uid)
else:
return render(request, 'abonapp/addInvoice.html', {
'csrf_token': csrf(request)['csrf_token'],
'abon': abon,
'invcount': models.InvoiceForPayment.objects.filter(abon=abon).count(),
'gid': gid
})
@login_required
def buy_tariff(request, gid, uid):
warntext = ''
frm = None
try:
if request.method == 'POST':
frm = forms.BuyTariff(request.POST)
if frm.is_valid():
cd = frm.cleaned_data
abon = get_object_or_404(models.Abon, id=uid)
abon.buy_tariff(cd['tariff'], request.user)
abon.save()
return redirect('abonhome_link', uid=uid)
else:
warntext = u'Что-то не так при покупке услуги, проверьте и попробуйте ещё'
else:
frm = forms.BuyTariff()
except models.LogicError as e:
warntext = e.value
except NetExcept as e:
warntext = e.value
return render(request, 'abonapp/buy_tariff.html', {
'warntext': warntext,
'form': frm or forms.BuyTariff(),
'uid': uid,
'gid': gid
})
@login_required
def chpriority(request, gid, uid):
t = request.GET.get('t')
act = request.GET.get('a')
current_abon_tariff = get_object_or_404(models.AbonTariff, id=t)
if act == 'up':
current_abon_tariff.priority_up()
elif act == 'down':
current_abon_tariff.priority_down()
return redirect('abonhome_link', gid=gid, uid=uid)
@login_required
def complete_service(request, gid, uid, srvid):
abtar = get_object_or_404(models.AbonTariff, id=srvid)
if abtar.abon.id != int(uid):
return HttpResponse('<h1>uid not equal uid from service</h1>')
try:
if request.method == 'POST':
# досрочно завершаем услугу
try:
abtar.finish_and_activate_next_tariff(request.user)
# завершаем текущую услугу.
abtar.delete()
except models.LogicError:
# Значит у абонента нет следующих услуг. Сигналим о закрытии инета в NAS
tc = get_TransmitterClientKlass()()
tc.signal_abon_close_inet(abtar.abon)
# Переупорядочиваем приоритеты
models.AbonTariff.objects.update_priorities(abtar.abon)
return redirect('abonhome_link', gid, uid)
next_tariff = models.AbonTariff.objects.filter(
abon=abtar.abon,
tariff_priority__gt=abtar.tariff_priority
)[:1]
if not abtar.time_start:
abtar.time_start = timezone.now()
abtar.save()
time_use = timezone.now() - abtar.time_start
time_use = {
'days': time_use.days,
'hours': time_use.seconds / 3600,
'minutes': time_use.seconds / 60 % 60
}
return render(request, 'abonapp/complete_service.html', {
'csrf_token': csrf(request)['csrf_token'],
'abtar': abtar,
'uid': uid,
'next_tariff': next_tariff[0] if next_tariff.count() > 0 else None,
'time_use': time_use,
'gid': gid
})
except models.LogicError as e:
warntext = e.value
except NetExcept as e:
warntext = e.value
return render(request, 'abonapp/complete_service.html', {
'csrf_token': csrf(request)['csrf_token'],
'abtar': abtar,
'uid': uid,
'warntext': warntext
})
@login_required
def log_page(request):
logs = models.AbonLog.objects.all()
logs = mydefs.pag_mn(request, logs)
return render(request, 'abonapp/log.html', {
'logs': logs
})
# API's
def abons(request):
ablist = map(lambda abn: {
'id': abn.id,
'tarif_id': abn.active_tariff().id if abn.active_tariff() else 0,
'ip': abn.ip_address.int_ip(),
'is_active': abn.is_active
}, models.Abon.objects.all())
tarlist = map(lambda trf: {
'id': trf.id,
'speedIn': trf.speedIn,
'speedOut': trf.speedOut
}, Tariff.objects.all())
data = {
'subscribers': ablist,
'tariffs': tarlist
}
del ablist, tarlist
return HttpResponse(dumps(data))

0
accounts_app/__init__.py

4
accounts_app/admin.py

@ -0,0 +1,4 @@
from django.contrib import admin
from models import UserProfile
admin.site.register(UserProfile)

42
accounts_app/migrations/0001_initial.py

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0007_alter_validators_add_error_messages'),
('photo_app', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(max_length=127, unique=True)),
('fio', models.CharField(max_length=256)),
('birth_day', models.DateField(auto_now_add=True)),
('is_active', models.BooleanField(default=True)),
('is_admin', models.BooleanField(default=False)),
('telephone', models.CharField(max_length=16, unique=True, validators=[django.core.validators.RegexValidator(b'^\\+[7,8,9,3]\\d{10,11}$')], verbose_name=b'Telephone number')),
('skype', models.CharField(blank=True, max_length=20)),
('avatar', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photo_app.Photo')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'abstract': False,
},
),
]

0
accounts_app/migrations/__init__.py

107
accounts_app/models.py

@ -0,0 +1,107 @@
# -*- coding:utf-8 -*-
from django.db import models
from djing.settings import DEFAULT_PICTURE
from photo_app.models import Photo
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.core.validators import RegexValidator
class MyUserManager(BaseUserManager):
def create_user(self, telephone, username, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not telephone:
raise ValueError('Users must have an telephone number')
user = self.model(
telephone=telephone,
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, telephone, username, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(telephone,
password=password,
username=username
)
user.is_admin = True
user.is_superuser = True
user.save(using=self._db)
return user
class UserProfile(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=127, unique=True)
fio = models.CharField(max_length=256)
birth_day = models.DateField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
telephone = models.CharField(
max_length=16,
verbose_name='Telephone number',
unique=True,
validators=[RegexValidator(r'^\+[7,8,9,3]\d{10,11}$')]
)
skype = models.CharField(max_length=20, blank=True)
avatar = models.ForeignKey(Photo, null=True, blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['telephone']
def get_full_name(self):
if self.fio:
return "%s: %s" % (self.username, self.fio)
else:
return self.username
def get_short_name(self):
return self.username or self.telephone
# Use UserManager to get the create_user method, etc.
objects = MyUserManager()
@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
def get_big_ava(self):
if self.avatar:
return self.avatar.big()
else:
return DEFAULT_PICTURE
def get_min_ava(self):
if self.avatar:
return self.avatar.min()
else:
return DEFAULT_PICTURE
def __unicode__(self):
return self.username
#from django.db.models.signals import post_save
'''def create_custom_user(sender, instance, created, **kwargs):
if created:
values = {}
for field in sender._meta.local_fields:
values[field.attname] = getattr(instance, field.attname)
user = UserProfile(**values)
user.save()'''
#post_save.connect(create_custom_user, User)

16
accounts_app/tests.py

@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

27
accounts_app/urls.py

@ -0,0 +1,27 @@
# -*- coding:utf-8 -*-
from django.conf.urls import url
import views
urlpatterns = [
url(r'^login/', views.to_signin, name='login_link'),
url(r'^logout/', views.sign_out, name='logout_link'),
url(r'^me$', views.profile_show, name='profile'),
url(r'^$', views.acc_list, name='accounts_list'),
url(r'^add$', views.create_profile, name='create_profile_link'),
url(r'^settings$', views.ch_info, name='settings_chinfo_link'),
url(r'^settings/change_ava$', views.ch_ava, name='settings_chava_link'),
url(r'^(?P<id>\d+)$', views.profile_show, name='other_profile'),
url(r'^(?P<id>\d+)/perms$', views.perms, name='profile_perms_link'),
url(r'^(?P<uid>\d+)/chgroup$', views.chgroup, name='profile_chgroup_link'),
url(r'^(?P<uid>\d+)/del$', views.delete_profile, name='delete_profile_link'),
url(r'^group/$', views.groups, name='profile_groups_list'),
url(r'^group/(?P<id>\d+)$', views.group, name='profile_group_link')
]

258
accounts_app/views.py

@ -0,0 +1,258 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.decorators import login_required#, permission_required
from django.contrib.auth import authenticate, login, logout
from photo_app.models import Photo
from django.core.urlresolvers import NoReverseMatch
from models import UserProfile
from django.shortcuts import render, redirect, get_object_or_404
from django.template.context_processors import csrf
from django.http import Http404
from django.contrib.auth.models import Group, Permission
import mydefs
@login_required
def home(request):
return redirect('profile')
def to_signin(request):
nextl = request.GET.get('next')
try:
if request.POST:
auser = authenticate(username=request.POST.get('login'), password=request.POST.get('password'))
if auser:
login(request, auser)
if nextl == 'None' or nextl == None or nextl == '':
return redirect('profile')
else:
return redirect(nextl)
else:
return render(request, 'accounts/login.html', {
'csrf_token': csrf(request)['csrf_token'],
'next': nextl,
'errmsg': u'Неправильный логин или пароль, попробуйте ещё раз'
})
except NoReverseMatch:
raise Http404(u"Destination page does not exist")
return render(request, 'accounts/login.html', {
'csrf_token': csrf(request)['csrf_token'],
'next': nextl
})
def sign_out(request):
logout(request)
return redirect('login_link')
@login_required
def profile_show(request, id=0):
id = mydefs.safe_int(id)
if id == 0:
usr = request.user
else:
usr = get_object_or_404(UserProfile, id=id)
if request.method == 'POST':
usr.username = request.POST.get('username')
usr.fio = request.POST.get('fio')
usr.telephone = request.POST.get('telephone')
usr.is_active = request.POST.get('stat')
usr.is_admin = request.POST.get('is_admin')
usr.save()
return redirect('other_profile', id=id)
return render(request, 'accounts/index.html', {
'uid': id,
'userprofile': usr
})
@login_required
def chgroup(request, uid):
usr = get_object_or_404(UserProfile, id=uid)
usergroups = usr.groups.all()
othergroups = filter(lambda g: g not in usergroups, Group.objects.all())
#Group.objects.exclude(user__in=usergroups)
return render(request, 'accounts/profile_chgroup.html', {
'uid': uid,
'userprofile': usr,
'allgroups': othergroups,
'usergroups': usergroups
})
@login_required
def ch_ava(request):
if request.method == 'POST':
user = request.user
if user.avatar:
user.avatar.delete()
photo = Photo()
photo.image = request.FILES.get('avatar')
photo.save()
user.avatar = photo
user.save()
request.user = user
return render(request, 'accounts/settings/ch_info.html', {
'user': request.user
})
@login_required
def ch_info(request):
warntext=''
if request.method == 'POST':
user = request.user
user.username = request.POST.get('username')
user.fio = request.POST.get('fio')
user.email = request.POST.get('email')
user.telephone = request.POST.get('telephone')
user.skype = request.POST.get('skype')
psw = request.POST.get('oldpasswd')
if psw != '':
if user.check_password(psw):
newpasswd = request.POST.get('newpasswd')
user.set_password(newpasswd)
else:
warntext = u'Неправильный пароль'
user.save()
request.user = user
return render(request, 'accounts/settings/ch_info.html', {
'user': request.user,
'warntext': warntext
})
@login_required
##@permission_required('accounts_app.add_userprofile')
def create_profile(request):
if request.method == 'POST':
username = request.POST.get('username')
user = UserProfile()
user.username = username
user.fio = request.POST.get('fio')
user.email = request.POST.get('email')
user.telephone = request.POST.get('telephone')
user.skype = request.POST.get('skype')
user.is_admin = True
passwd = request.POST.get('passwd')
conpasswd = request.POST.get('conpasswd')
if not passwd:
return render(request, 'accounts/create_acc.html',{
'warntext': u'Забыли указать пароль для нового аккаунта',
'csrf_token': csrf(request)['csrf_token'],
'newuser': user
})
if not conpasswd:
return render(request, 'accounts/create_acc.html',{
'warntext': u'Забыли повторить пароль для нового аккаунта',
'csrf_token': csrf(request)['csrf_token'],
'newuser': user
})
if passwd == conpasswd:
user_qs = UserProfile.objects.filter(username=username)[:1]
if user_qs.count() == 0:
user.set_password(passwd)
user.save()
return redirect('accounts_list')
else:
return render(request, 'accounts/create_acc.html',{
'warntext': u'Пользователь с таким именем уже есть',
'csrf_token': csrf(request)['csrf_token'],
'newuser': user
})
else:
return render(request, 'accounts/create_acc.html',{
'warntext': u'Пароли не совпадают, попробуйте ещё раз',
'csrf_token': csrf(request)['csrf_token'],
'newuser': user
})
return render(request, 'accounts/create_acc.html', {'csrf_token': csrf(request)['csrf_token'],})
@login_required
#@permission_required('accounts_app.del_userprofile')
def delete_profile(request, uid):
prf = get_object_or_404(UserProfile, id=uid)
prf.delete()
return redirect('accounts_list')
@login_required
def acc_list(request):
users = UserProfile.objects.filter(is_admin=True)
users = mydefs.pag_mn(request, users)
return render(request, 'accounts/acc_list.html', {
'users': users
})
@login_required
#@permission_required('accounts_app.change_userprofile')
def perms(request, id):
ingroups = filter(lambda x: x[0] == 'ingroups', request.POST.lists())[0][1]
id = mydefs.safe_int(id)
profile = get_object_or_404(UserProfile, id=id)
profile.groups.clear()
for group_id in ingroups:
gid = mydefs.safe_int(group_id)
profile.groups.add(gid)
profile.save()
return redirect('other_profile', id)
@login_required
def groups(request):
grps = Group.objects.all()
grps = mydefs.pag_mn(request, grps)
return render(request, 'accounts/group_list.html', {
'groups': grps
})
@login_required
#@permission_required('auth.change_group')
def group(request, id):
id = mydefs.safe_int(id)
grp = get_object_or_404(Group, id=id)
if request.method == 'POST':
group_rights = filter(lambda x: x[0] == 'group_rights', request.POST.lists())[0][1]
grp.permissions.clear()
for grr in group_rights:
rid = mydefs.safe_int(grr)
grp.permissions.add(rid)
grp.save()
return redirect('profile_group_link', id=id)
grp_rights = grp.permissions.all()
all_rights = Permission.objects.exclude(group=grp)
#prms = Permission.objects.all()
#for pr in prms:
# print u"%s | %s" % (pr.name, pr.codename)
return render(request, 'accounts/group.html', {
'csrf_token': csrf(request)['csrf_token'],
'group': grp,
'all_rights': all_rights,
'grp_rights': grp_rights
})

12
agent.py

@ -0,0 +1,12 @@
#!/bin/env python
import os
from agent import main
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
while True:
main(debug=True)
print "Exit from main, reload..."

3
agent/__init__.py

@ -0,0 +1,3 @@
from models import Abonent, Tariff
from main import main
from sslTransmitter import get_TransmitterClientKlass, NetExcept

27
agent/db.py

@ -0,0 +1,27 @@
# -*- coding:utf-8 -*-
import requests
from json import loads
from models import deserialize_tariffs, deserialize_abonents
import settings
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:
user_data = loads(r.text)
del r
# Получаем тарифы
tariffs = deserialize_tariffs(user_data)
# Получаем пользователей
abons = deserialize_abonents(user_data, tariffs)
return abons, tariffs
except ValueError as e:
print 'Error:', e, r.text
return

48
agent/firewall.py

@ -0,0 +1,48 @@
# -*- coding:utf-8 -*-
class FirewallManager(object):
f = r'/sbin/ipfw -q'
# вызывает комманду shell
def exec_cmd(self, cmd):
print cmd
#os.execv(cmd, [''])
# ставит заглушку на абонента
def set_cap(self, user):
pass
# Открывает доступ в интернет
def open_inet_door(self, user):
if not user.tariff:
print u'WARNING: User does not have a tariff'
return
cmd = r"%s table 12 add %s/32 %d && %s table 13 add %s/32 %d" % (
self.f, user.ip_str(), user.tariff.tid,
self.f, user.ip_str(), user.tariff.tid+1000
)
self.exec_cmd(cmd)
# Закрывает доступ в интернет
def close_inet_door(self, user):
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.exec_cmd(cmd)
# Создаёт тариф (пайпы, режущие скорость
def make_tariff(self, tariff):
cmd = r"make ipfw tariff :)"
self.exec_cmd(cmd)
# Убирает тариф из фаервола
def destroy_tariff(self, tariff):
cmd = r"destroy ipfw tariff :)"
self.exec_cmd(cmd)
def reset(self):
cmd = r"%s -f flush && %s table all flush" % (self.f, self.f)
self.exec_cmd(cmd)

72
agent/ipfw.sh

@ -0,0 +1,72 @@
#!/bin/sh
#########################################################
# ВАЖНО! Биллинг пока ограничен количеством тарифных планов
# не больше 1000
#########################################################
f="/sbin/ipfw -q"
lan=em1 # Clients
wan=em0 # Inet
${f} -f flush
${f} table all flush
sysctl net.inet.ip.fw.one_pass=0
# dns
${f} table 100 add 8.8.8.8 # google public dns
${f} table 100 add 8.8.4.4 # google public dns2
${f} table 100 add 77.88.8.8 # yandex base dns
${f} table 100 add 77.88.8.1 # yandex base dns2
# ssh access
${f} add 50 allow tcp from any to me 22
${f} add 51 allow tcp from me 22 to any
# loopback
${f} add 100 allow ip from any to any via lo0
# в таблице 100 приоритетный траффик.
# это dns, платёжки..
${f} add 500 allow ip from table\(100\) to any
${f} add 501 allow ip from any to table\(100\)
# в таблице 10 разрешённые пользователи
# блокируем трафик всем кто не в ней
${f} add 1001 deny ip from not table\(10\) to any via $wan
# если у абонентов есть внешние адреса (не через NAT)
#${f} add 1101 deny ip from any to not table\(10\) via $wan
# по 2 пайпа на тарифный план, на вход и выход
#${f} pipe 212 config bw 1152Kbit/s mask src-ip 0xffffffff noerror
#${f} pipe 213 config bw 1152Kbit/s mask dst-ip 0xffffffff noerror
# добавляем пайпы в таблицу
${f} add 2001 pipe 212 ip from table\(10\) to any via $wan
${f} add 2002 pipe 213 ip from any to table\(11\) via $wan
#----------------------
# так добавляем абонентов чтоб резать скорость, надо указать номер их пайпа
#${f} table 10 add 10.0.172.138/32 212
#${f} table 11 add 10.0.172.138/32 2212
#----------------------
# тут будем поджимать пользователей когда не хватает канала

120
agent/main.py

@ -0,0 +1,120 @@
# -*- coding:utf-8 -*-
from sys import stdout
from db import load_from_db
from firewall import FirewallManager
from time import sleep
from sslTransmitter import TransmitServer
def filter_user_by_id(users, uid):
usrs = filter(lambda usr: usr.uid == uid, users)
if len(usrs) > 0:
return usrs[0]
else:
return
def main(debug=False):
users, tariffs = load_from_db()
frw = FirewallManager()
frw.reset()
# Инициализация абонентов
if debug:
print u'Инициализация...'
# Открываем доступ в инет тем кто активен и у кого подключён тариф
for usr in filter(lambda usr: usr.is_active, users):
# Доступ в интернет происходит по наличию подключённого тарифа
# если тарифа нет, то и инета нет
if usr.tariff:
# Открываем доступ в инет
frw.open_inet_door(usr)
# Слушем в отдельном процессе сеть на предмет событий
ts = TransmitServer('127.0.0.1', 2134)
ts.start()
if debug:
print u"Загружено %d абонентов" % len(users)
while True:
# Загружаем события для абонентов из сети (список объектов EventNAS из models)
events = ts.get_data()
# Проходим по появившимся событиям
for event in events:
#event.toa
#event.id
#event.dt
# Смотрим тип события
toa = int(event.toa)
if toa == 0: continue
# Включаем абонента
elif toa == 1:
usr = filter_user_by_id(users, event.id)
# Открываем доступ в инет
frw.open_inet_door(usr)
# Выключаем абонента
elif toa == 2:
usr = filter_user_by_id(users, event.id)
# Выключаем интернет
frw.close_inet_door(usr)
# Сообщение на заглушку
elif toa == 3:
usr = filter_user_by_id(users, event.id)
# Ставим заглушку
frw.set_cap(usr)
# Выключаем интернет
frw.close_inet_door(usr)
# Открываем доступ в инет
elif toa == 4:
usr = filter_user_by_id(users, event.id)
frw.close_inet_door(usr)
frw.open_inet_door(usr)
# Закрываем доступ в инет
elif toa == 5:
usr = filter_user_by_id(users, event.id)
frw.close_inet_door(usr)
elif toa == 6:
# Сигнал на перезагрузку
# Выходим из main, выше она в цикле запустится ещё раз
return
elif toa == 7:
# Сигнал о том что инфа об абоненте изменилась, надо перечитать
usr = filter_user_by_id(users, event.id)
usr.deserialize(event.dt, tariffs)
# если абонент активен, и куплен и активирован тариф то можно и в инет
if usr.is_active and usr.tariff is not None:
frw.close_inet_door(usr)
frw.open_inet_door(usr)
elif toa == 8:
# Сигнал об изменении данных в тарифе
tariff = filter(lambda trf: trf.tid == event.id, tariffs)
if len(tariff) > 0:
tariff = tariff[0]
tariff.deserialize(event.dt)
# Пересоздаём тариф
frw.destroy_tariff(tariff)
frw.make_tariff(tariff)
else:
print 'WARNING: не найден тариф для которого сигнал на изменение данных, пробуем перезагрузиться'
return
# Очищаем очередь событий
ts.clear()
# ждём время между итерациями проверки 10 сек...
sleep(10)
stdout.write('.')
stdout.flush()

143
agent/models.py

@ -0,0 +1,143 @@
# -*- coding:utf-8 -*-
import socket
import struct
from json import loads, dumps
from abc import ABCMeta, abstractmethod
class Serializer(object):
__metaclass__ = ABCMeta
@abstractmethod
def _serializable_obj(self):
"""Вернуть словарь для сериализации"""
def serialize(self):
return dumps(self._serializable_obj())
@abstractmethod
def deserialize(self, *args):
"""Надо обязательно этот метод реализовать, он много где используется.
Из JSON создать объект класса где реализуется метод"""
def serialize_tariffs(tariffs):
dt = map(lambda trf: trf._serializable_obj(), tariffs)
return dumps({'tariffs': dt})
def deserialize_tariffs(dat):
dat = loads(dat) if type(dat) == str else dat
# Распаковываем из JSON массива dat['tariffs'] объекты через метод deserialize
return map(lambda tariff: Tariff().deserialize(tariff), dat['tariffs'])
def serialize_abonents(abonents):
dt = map(lambda abn: abn._serializable_obj(), abonents)
return dumps({'subscribers': dt})
def deserialize_abonents(dat, tariffs):
dat = loads(dat) if type(dat) == str else dat
# Распаковываем из JSON массива dat['subscribers'] объекты через метод deserialize
return map(lambda abon: Abonent().deserialize(abon, tariffs), dat['subscribers'])
class Tariff(Serializer):
tid = 0
speedIn = 0.0
speedOut = 0.0
def __init__(self, tariff_id=None, speed_in=None, speed_out=None):
self.tid = tariff_id
self.speedOut = speed_out
self.speedIn = speed_in
def is_active(self):
"""возвращает активность тарифа. Если он не активен то пропустить"""
return True
def _serializable_obj(self):
return {
'id': self.tid,
'speedIn': self.speedIn,
'speedOut': self.speedOut
}
def deserialize(self, dump):
inf = loads(dump) if type(dump) == str else dump
self.speedIn = float(inf['speedIn'])
self.speedOut = float(inf['speedOut'])
self.tid = int(inf['id'])
return self
class Abonent(Serializer):
uid = 0
tariff = Tariff()
ip = 0xffffffff
# Включён-ли абонент
is_active = True
def __init__(self, uid=None, ip=None, tariff=None):
self.ip = ip
self.uid = uid
self.tariff = tariff
def ip_str(self):
# int2ip, Example out '127.0.0.1'
return socket.inet_ntoa(struct.pack("!I", self.ip))
def _serializable_obj(self):
return {
'id': self.uid,
'is_active': self.is_active,
'ip': self.ip,
'tarif_id': self.tariff.tid if self.tariff else 0
}
def deserialize(self, dump, tariffs):
inf = loads(dump) if type(dump) == str else dump
self.uid = int(inf['id'])
self.is_active = bool(inf['is_active'])
self.ip = int(inf['ip'])
tarif_id = int(inf['tarif_id'])
dbtrf = filter(lambda trf: trf.tid == tarif_id, tariffs)
if len(dbtrf) > 0:
self.tariff = dbtrf[0]
else:
self.tariff = None
return self
class EventNAS(Serializer):
# Type Of Action
toa = 0
# id of object
id = 0
# extended data
dt = object()
def __init__(self, type_action=None, obj_id=None, ext_data=None):
self.toa = type_action
self.id = obj_id
self.dt = ext_data
def _serializable_obj(self):
if self.dt:
return {'toa': self.toa, 'id': self.id, 'dt': self.dt}
else:
return {'toa': self.toa, 'id': self.id}
def deserialize(self, dump):
print dump
inf = loads(dump) if type(dump) == str else dump
self.toa = int(inf['toa'])
self.id = int(inf['id'])
self.dt = inf.get('dt')
return self

0
agent/netflow/__init__.py

49
agent/netflow/netflow_handler.py

@ -0,0 +1,49 @@
#!/bin/env python2
import sys
import socket
import struct
from re import sub
def ip2int(strip):
return struct.unpack("!I", socket.inet_aton(strip))[0]
def convert(query):
dat = sub(r'\s+', ' ', query.strip('\n')).split(' ')
if len(dat) == 1:
return
src_ip = ip2int(dat[0])
dst_ip = ip2int(dat[1])
proto = int(dat[2])
src_port = int(dat[3])
dst_port = int(dat[4])
octets = int(dat[5])
packets = int(dat[6])
sql = ",(%s,%s,%d,%d,%d,%d,%d)" % (
hex(src_ip), hex(dst_ip), proto, src_port, dst_port, octets, packets
)
return sql
if __name__=='__main__':
f=sys.stdin
print("INSERT INTO flowstat(`src_ip`, `dst_ip`, `proto`, `src_port`, `dst_port`, `octets`, `packets`) VALUES")
# always none
f.readline()
# first line
rs = convert(f.readline())
# without first comma
print(rs[1:])
while True:
rs = convert(f.readline())
if not rs:
break
print(rs)
f.close()

11
agent/netflow/netflow_handler.sh

@ -0,0 +1,11 @@
#!/bin/sh
DUMP_DIR="/var/db/flows"
DUMP_FILE="$DUMP_DIR/$1"
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin
CUR_DIR=`dirname $0`
flow-print -f3 < ${DUMP_FILE} | ${CUR_DIR}/netflow_handler.py \
| mysql -uroot -p jungagent --password=ps

7
agent/netflow/start_netflow.sh

@ -0,0 +1,7 @@
#!/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/home/dn/bin
flow-capture -R /home/dn/bin/netflow_handler.py -w /var/db/flows -n1 -N0 0.0.0.0/0.0.0.0/8888
softflowd -v 5 -i wlp3s0 -n 127.0.0.1:8888

17
agent/settings.py

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Agent ip and port
SELF_IP = '127.0.0.1'
SELF_PORT = 2134
# Main server ip and port
SERVER_IP = '127.0.0.1'
SERVER_PORT = 8000 #8443
# Certificates
CERTFILE = "/etc/ssl/server.crt"
KEYFILE = "/etc/ssl/server.key"
# Использовать-ли при передаче инфы между NAS и основным сервером SSL
IS_USE_SSL = False

226
agent/sslTransmitter.py

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
import ssl
import socket
from multiprocessing import Process, Manager#, Lock
import settings
from models import EventNAS, Abonent, Tariff
class NetExcept(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class SSLTransmitterServer(object):
bindsocket = None
def connect(self, ip, port):
bindsocket = socket.socket()
bindsocket.bind((ip, port))
bindsocket.listen(5)
self.bindsocket = bindsocket
def _on_data_recive(self, v, data):
print "do_something:", data
#with lock:
v.append(EventNAS().deserialize(data))
return False
def _deal_with_client(self, connstream, v):
data = connstream.read()
while data:
if not self._on_data_recive(v, data):
break
data = connstream.read()
def process(self, v):
while True:
newsocket, fromaddr = self.bindsocket.accept()
connstream = ssl.wrap_socket(newsocket,
server_side=True,
certfile=settings.CERTFILE,
keyfile=settings.KEYFILE)
try:
self._deal_with_client(connstream, v)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
class PlainTransmitterServer(SSLTransmitterServer):
def process(self, v):
while True:
newsocket, fromaddr = self.bindsocket.accept()
dat = newsocket.recv(0xffff)
if not dat:
break
self._on_data_recive(v, dat)
# Декоратор переводит классы абонента базы к объекту агента если надо.
# abonapp.models.Abon -> agent.Abonent
def agent_abon_typer(fn):
def wrapped(self, abon):
if isinstance(abon, Abonent):
fn(self, abon)
else:
abn = Abonent(
abon.id,
abon.ip_address.int_ip() if abon.ip_address else 0,
abon.active_tariff()
)
fn(self, abn)
return wrapped
# Декоратор переводит классы тарифа базы к объекту агента если надо.
# tariff_app.models.Tariff -> agent.Tariff
def agent_tarif_typer(fn):
def wrapped(self, tariff):
if isinstance(tariff, Tariff):
fn(self, tariff)
else:
trf = Tariff(
tariff.id,
tariff.speedIn,
tariff.speedOut
)
fn(self, trf)
return wrapped
class SSLTransmitterClient(object):
s = None
def __init__(self, ip=None, port=None):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Require a certificate from the server. We used a self-signed certificate
# so here ca_certs must be the server certificate itself.
self.s = ssl.wrap_socket(s,
ca_certs=settings.CERTFILE,
cert_reqs=ssl.CERT_REQUIRED)
self.s.connect((
ip or settings.SELF_IP,
port or settings.SELF_PORT
))
except socket.error:
raise NetExcept(u'Ошибка подключения к NAS агенту')
def write(self, d):
self.s.write(d)
@agent_abon_typer
def signal_abon_enable(self, abon):
self.write(
EventNAS(1, abon.uid).serialize()
)
@agent_abon_typer
def signal_abon_disable(self, abon):
self.write(
EventNAS(2, abon.uid).serialize()
)
@agent_abon_typer
def signal_abon_set_cap(self, abon):
self.write(
EventNAS(3, abon.uid).serialize()
)
@agent_abon_typer
def signal_abon_open_inet(self, abon):
self.write(
EventNAS(4, abon.uid).serialize()
)
@agent_abon_typer
def signal_abon_close_inet(self, abon):
self.write(
EventNAS(5, abon.uid).serialize()
)
@agent_abon_typer
def signal_agent_reboot(self, abon):
self.write(
EventNAS(6, abon.uid).serialize()
)
@agent_abon_typer
def signal_abon_refresh_info(self, abon):
self.write(
EventNAS(7, abon.uid, abon._serializable_obj()).serialize()
)
@agent_tarif_typer
def signal_tarif_refresh_info(self, tariff):
self.write(
EventNAS(8, tariff.tid, tariff._serializable_obj()).serialize()
)
def __del__(self):
self.s.close()
class PlainTransmitterClient(SSLTransmitterClient):
def __init__(self, ip=None, port=None):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((
ip or settings.SELF_IP,
port or settings.SELF_PORT
))
self.s = s
except socket.error:
raise NetExcept(u'Ошибка подключения к NAS агенту')
def write(self, d):
self.s.send(d)
def get_TransmitterClientKlass():
if settings.IS_USE_SSL:
return SSLTransmitterClient
else:
return PlainTransmitterClient
def get_TransmitterServerKlass():
if settings.IS_USE_SSL:
return SSLTransmitterServer
else:
return PlainTransmitterServer
def proc_entrypoint(obj, v, lock, ip, port):
srv = get_TransmitterServerKlass()()
srv.connect(ip, port)
srv.process(v)
class TransmitServer(object):
def __init__(self, ip, port):
mngr = Manager()
self.v = mngr.list()
#self.lock = Lock()
self.p = Process(target=proc_entrypoint, args=(self, self.v, None, ip, port))#self.lock))
def get_data(self):
if len(self.v) > 0:
return list(self.v)
else:
return []
def clear(self):
del self.v[:]
def start(self):
self.p.start()
def __del__(self):
self.p.terminate()

1
agent/tariff.py

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

11
bugs.txt

@ -0,0 +1,11 @@
- (GUI) Иконки возле кнопок не настроены, натыканы случайно
Остановился на управлении купленными услугами(тарифами):
+ нужен порядок выполнения очереди тарифов (модель зделана)
+ Управление приоритетом тарифов (порядком выполнения)
- удаление ещё не активных купленных тарифов
+ отображение и выделение активного тарифа
Нужно на каждое изменение данных об абоненте слать сигнал об этом на NAS
Фаервол допилить, там с пайпами не понятно, не знаю принцип действия

18
cron.py

@ -0,0 +1,18 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import django
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
django.setup()
from abonapp.models import Abon, AbonTariff
users = Abon.objects.all()
for usr in users:
usr.activate_next_tariff()
AbonTariff.objects.update_priorities(usr)

0
devapp/__init__.py

6
devapp/admin.py

@ -0,0 +1,6 @@
from django.contrib import admin
import models
admin.site.register(models.Device)
admin.site.register(models.Port)
admin.site.register(models.PortStates)

7
devapp/apps.py

@ -0,0 +1,7 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class DevappConfig(AppConfig):
name = 'devapp'

109
devapp/base_intr.py

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
from netsnmp import Session, VarList, Varbind
class DevBase(object):
__metaclass__ = ABCMeta
@staticmethod
def description():
"""Возвращает текстовое описание"""
@abstractmethod
def reboot(self):
"""Перезагружает устройство"""
@abstractmethod
def get_statistics(self):
"""Получаем статистику"""
@abstractmethod
def get_vlan(self):
"""Получаем инфу о VLAN"""
@abstractmethod
def get_ports(self):
"""Получаем инфу о портах"""
@abstractmethod
def toggle_port(self, port_num):
pass
class Port(object):
def __init__(self, num, name, status, mac, speed):
self.num = int(num)
self.nm = name
self.st = status
self._mac = mac
self.sp = speed
def mac(self):
m = self._mac
return "%x:%x:%x:%x:%x:%x" % (ord(m[0]), ord(m[1]), ord(m[2]), ord(m[3]), ord(m[4]), ord(m[5]))
class SNMPBaseWorker(object):
ses = None
oids = {
'reboot': '.1.3.6.1.4.1.2021.8.1.101.1',
'get_ports': {
'names': 'IF-MIB::ifDescr',
'stats': 'IF-MIB::ifAdminStatus',
'macs': 'IF-MIB::ifPhysAddress',
'speeds': 'IF-MIB::ifHighSpeed'
},
'name': 'SNMPv2-SMI::mib-2.47.1.1.1.1.7.1',
'toggle_port': '.1.3.6.1.2.1.2.2.1.7'
}
def __init__(self, ip, passw='public', ver=2):
self.ses = Session(DestHost=ip, Version=ver, Community=passw)
def _get_vars(self, oid):
vs = VarList(Varbind(oid))
return self.ses.walk(vs)
def _get_var(self, oid):
var = VarList(Varbind(oid))
return self.ses.get(var)
# Enable/Disable port
def toggle_port(self, port_num, status=True):
vs = VarList(Varbind(
tag="%s.%d" % (self.oids['toggle_port'], port_num),
val=1 if status else 2,
type='INTEGER'
))
return self.ses.set(vs)
def get_ports(self):
nams = self._get_vars(self.oids['get_ports']['names'])
stats = self._get_vars(self.oids['get_ports']['stats'])
macs = self._get_vars(self.oids['get_ports']['macs'])
speeds = self._get_vars(self.oids['get_ports']['speeds'])
res = []
ln = len(nams)
for n in range(0, ln):
res.append(Port(n, nams[n], bool(stats[n]), macs[n], int(speeds[n])))
return res
def get_name(self):
return self._get_var(self.oids['name'])
# Example usage
if __name__ == '__main__':
wrk = SNMPBaseWorker('10.115.1.104', 'private', 2)
print(wrk.get_name())
for pr in wrk.get_ports():
assert isinstance(pr, Port)
print(pr.nm, pr.st, pr.mac(), pr.sp)
# Enable 2 port
print wrk.toggle_port(2, True)
# Disable 2 port
print wrk.toggle_port(2, False)

30
devapp/dev_types.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from base_intr import DevBase, SNMPBaseWorker
class DLinkDevice(DevBase):
@staticmethod
def description():
return u"Свич D'Link"
def reboot(self):
pass
DEVICE_TYPES = (
('Dl', DLinkDevice),
)
class SNMPDlinkWorker(SNMPBaseWorker):
oids = {
'reboot': '.1.3.6.1.4.1.2021.8.1.101.1',
'get_ports': {
'names': 'IF-MIB::ifDescr',
'stats': 'IF-MIB::ifAdminStatus',
'macs': 'IF-MIB::ifPhysAddress',
'speeds': 'IF-MIB::ifHighSpeed'
},
'name': 'SNMPv2-SMI::mib-2.47.1.1.1.1.7.1'
}

32
devapp/forms.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from django import forms
import models
from mydefs import ip_addr_regex
class DeviceForm(forms.ModelForm):
class Meta:
model = models.Device
fields = '__all__'
widgets = {
'ip_address': forms.TextInput(attrs={
'pattern': ip_addr_regex,
'placeholder': '192.168.0.100',
'required': True,
'class': 'form-control',
'id': 'ip_address'
}),
'comment': forms.Textarea(attrs={
'required': True,
'class': 'form-control',
'id': 'comment'
}),
'devtype': forms.Select(attrs={
'class': 'form-control',
'id': 'devtype'
}),
'man_passw': forms.PasswordInput(attrs={
'class': 'form-control',
'id': 'man_passw'
})
}

49
devapp/migrations/0001_initial.py

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import mydefs
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Device',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip_address', mydefs.MyGenericIPAddressField(max_length=8, protocol=b'IPv4')),
('comment', models.CharField(max_length=256)),
('devtype', models.CharField(choices=[(b'Dl', "\u0421\u0432\u0438\u0447 D'Link")], default=b'Dl', max_length=2)),
('man_passw', models.CharField(blank=True, max_length=16, null=True)),
],
),
migrations.CreateModel(
name='Port',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('num', models.PositiveSmallIntegerField(default=0)),
('speed', models.CharField(choices=[(b'h', b'100Mbps'), (b'k', b'1Gbps'), (b'd', b'10Gbps')], default=b'h', max_length=1)),
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='devapp.Device')),
],
),
migrations.CreateModel(
name='PortStates',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('state_json_info', models.TextField()),
('port', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='devapp.Port')),
],
),
migrations.AlterUniqueTogether(
name='port',
unique_together=set([('device', 'num')]),
),
]

0
devapp/migrations/__init__.py

52
devapp/models.py

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
from django.db import models
from base_intr import DevBase
from mydefs import MyGenericIPAddressField, MyChoicesAdapter
from dev_types import DEVICE_TYPES
class _DeviceChoicesAdapter(MyChoicesAdapter):
def __init__(self):
super(_DeviceChoicesAdapter, self).__init__(DEVICE_TYPES)
class Device(models.Model):
ip_address = MyGenericIPAddressField()
comment = models.CharField(max_length=256)
devtype = models.CharField(max_length=2, default=DEVICE_TYPES[0][0], choices=_DeviceChoicesAdapter())
man_passw = models.CharField(max_length=16, null=True, blank=True)
#map_dot = models.ForeignKey()
def get_abons(self):
pass
def get_stat(self):
pass
def get_manager_klass(self):
klasses = filter(lambda kl: kl[0] == self.devtype, DEVICE_TYPES)
if len(klasses) > 0:
res = klasses[0][1]
if issubclass(res, DevBase):
return res
return
class Port(models.Model):
PORT_SPEEDS = (
('h', '100Mbps'),
('k', '1Gbps'),
('d', '10Gbps')
)
device = models.ForeignKey(Device)
num = models.PositiveSmallIntegerField(default=0)
speed = models.CharField(max_length=1, default=PORT_SPEEDS[0][0], choices=PORT_SPEEDS)
class Meta:
unique_together = (('device', 'num'))
class PortStates(models.Model):
port = models.OneToOneField(Port)
state_json_info = models.TextField()

3
devapp/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
devapp/urls.py

@ -0,0 +1,10 @@
from django.conf.urls import url
import views
urlpatterns = [
url(r'^$', views.devices, name='devs_link'),
url(r'^add$', views.dev, name='devs_add_link'),
url(r'^(?P<did>\d+)$', views.devview, name='devs_view_link'),
url(r'^(?P<did>\d+)/del$', views.devdel, name='devs_del_link'),
url(r'^(?P<devid>\d+)/edit$', views.dev, name='devs_edit_link'),
]

59
devapp/views.py

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect, get_object_or_404
from models import Device
from mydefs import pag_mn, res_success, res_error
from forms import DeviceForm
@login_required
def devices(request):
devs = Device.objects.all()
devs = pag_mn(request, devs)
return render(request, 'devapp/devices.html', {
'devices': devs
})
@login_required
def devdel(request, did):
try:
get_object_or_404(Device, id=did).delete()
return res_success(request, 'devs_link')
except:
return res_error(request, u'Неизвестная ошибка при удалении :(')
@login_required
def dev(request, devid=0):
warntext = ''
devinst = get_object_or_404(Device, id=devid) if devid != 0 else None
if request.method == 'POST':
frm = DeviceForm(request.POST, instance=devinst)
if frm.is_valid():
frm.save()
return redirect('devs_link')
else:
warntext = u'Ошибка в данных, проверте их ещё раз'
else:
frm = DeviceForm(instance=devinst)
return render(request, 'devapp/dev.html', {
'warntext': warntext,
'form': frm,
'devid': devid
})
@login_required
def devview(request, did):
warntext = ''
dev = get_object_or_404(Device, id=did)
return render(request, 'devapp/ports.html', {
'warntext': warntext,
'dev': dev
})

0
djing/__init__.py

143
djing/settings.py

@ -0,0 +1,143 @@
# -*- coding: utf-8 -*
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
from django.core.urlresolvers import reverse_lazy
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '$xvppe_5&iu4fgnj2h@eie6+w*n&m=60e7k_6ha5r4rgnfndz1'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'accounts_app',
'photo_app',
'privatemessage',
'abonapp',
'tariff_app',
'ip_pool',
'searchapp',
'devapp',
'mapapp',
'statistics'
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'djing.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'mydefs.context_processor_client_ipaddress'
],
},
},
]
WSGI_APPLICATION = 'djing.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
#'ENGINE': 'django.db.backends.sqlite3',
#'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': 'django.db.backends.mysql',
'NAME': 'jungagent',
'USER': 'root',
'PASSWORD': 'ps',
'HOST': 'localhost'
}
}
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'ru-RU'
TIME_ZONE = 'Europe/Simferopol'
USE_I18N = True
USE_L10N = False
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'
if DEBUG:
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
# Пример вывода: 16 сентября 2012
DATE_FORMAT = 'd E Y'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
DEFAULT_PICTURE = '/static/images/default-avatar.png'
AUTH_USER_MODEL = 'accounts_app.UserProfile'
LOGIN_URL = reverse_lazy('login_link')
LOGIN_REDIRECT_URL = reverse_lazy('profile')
LOGOUT_URL = reverse_lazy('logout_link')

26
djing/urls.py

@ -0,0 +1,26 @@
from django.conf.urls import url, include
from django.contrib import admin
import settings
from views import home
urlpatterns = [
url(r'^$', home),
url(r'^accounts/', include('accounts_app.urls')),
url(r'^im/', include('privatemessage.urls')),
url(r'^abons/', include('abonapp.urls')),
url(r'^tarifs/', include('tariff_app.urls')),
url(r'^ip_pool/', include('ip_pool.urls')),
url(r'^search/', include('searchapp.urls')),
url(r'^dev/', include('devapp.urls')),
url(r'^map/', include('mapapp.urls')),
url(r'^statistic/', include('statistics.urls')),
url(r'^admin/', admin.site.urls),
]
if settings.DEBUG:
from django.conf.urls.static import static
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += staticfiles_urlpatterns()

9
djing/views.py

@ -0,0 +1,9 @@
from django.shortcuts import redirect
def home(request):
return redirect('profile')
def finance_report(request):
pass

16
djing/wsgi.py

@ -0,0 +1,16 @@
"""
WSGI config for djing project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
application = get_wsgi_application()

17
install.sql

@ -0,0 +1,17 @@
create table flowstat (
`id` int(10) AUTO_INCREMENT NOT NULL,
`src_ip` CHAR(8) NOT NULL,
`dst_ip` CHAR(8) NOT NULL,
`proto` smallint(2) unsigned NOT NULL DEFAULT 0,
`src_port` smallint(5) unsigned NOT NULL DEFAULT 0,
`dst_port` smallint(5) unsigned NOT NULL DEFAULT 0,
`octets` INT unsigned NOT NULL DEFAULT 0,
`packets` INT unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO flowstat(`src_ip`, `dst_ip`, `proto`, `src_port`, `dst_port`, `octets`, `packets`) VALUES
('c0a80201', 'c0a805ba', 6, 49150, 443, 5281, 13),
('c0a80201', 'c0a805ba', 6, 49150, 443, 5281, 13)

1
ip_pool/__init__.py

@ -0,0 +1 @@
default_app_config = 'ip_pool.apps.IpPoolConfig'

4
ip_pool/admin.py

@ -0,0 +1,4 @@
from django.contrib import admin
import models
admin.site.register(models.IpPoolItem)

6
ip_pool/apps.py

@ -0,0 +1,6 @@
from django.apps import AppConfig
class IpPoolConfig(AppConfig):
name = 'ip_pool'
verbose_name = u"Ip pool"

21
ip_pool/forms.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from django import forms
from mydefs import ip_addr_regex
class PoolForm(forms.Form):
start_ip = forms.GenericIPAddressField(protocol='IPv4', widget=forms.TextInput(attrs={
'pattern': ip_addr_regex,
'placeholder': u'127.0.0.1',
'id': 'start_ip',
'class': 'form-control',
'required': ''
}), required=True)
end_ip = forms.GenericIPAddressField(protocol='IPv4', widget=forms.TextInput(attrs={
'pattern': ip_addr_regex,
'placeholder': u'127.0.0.1',
'id': 'end_ip',
'class': 'form-control',
'required': ''
}), required=True)

24
ip_pool/migrations/0001_initial.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
from django.db import migrations, models
import mydefs
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='IpPoolItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('ip', mydefs.MyGenericIPAddressField(max_length=8, protocol=b'IPv4')),
],
),
]

0
ip_pool/migrations/__init__.py

60
ip_pool/models.py

@ -0,0 +1,60 @@
from django.db import models, connection
from mydefs import ip2int, MyGenericIPAddressField
class IpPoolItemManager(models.Manager):
def get_pools(self):
ips = self.raw(r'SELECT id, ip FROM ip_pool_ippoolitem ORDER BY id')
ips_len = len(list(ips))
if ips_len < 1:
return
last_dg = ip2int(ips[0].ip)
start_pool = last_dg
res = list()
cnt = 0
for ip in ips:
ipnt = ip2int(ip.ip)
if ipnt > last_dg + 1 or ipnt < last_dg - 1:
res.append((start_pool, last_dg, cnt))
start_pool = ipnt
cnt = 0
last_dg = ipnt
cnt += 1
res.append((start_pool, last_dg, cnt))
return res
def add_pool(self, start_ip, end_ip):
start_ip = ip2int(start_ip)
end_ip = ip2int(end_ip)
if (end_ip - start_ip) > 5000:
raise Exception(u'Not add over 5000 ip\'s')
sql_strs = map(lambda tip: r"(%d)" % tip, range(start_ip, end_ip+1))
sql = r'INSERT INTO ip_pool_ippoolitem (ip) VALUES %s' % r",".join(sql_strs)
print sql
cursor = connection.cursor()
cursor.execute(sql)
def get_free_ip(self):
sql = r'SELECT ip_pool_ippoolitem.id as id, ip_pool_ippoolitem.ip as ip FROM ip_pool_ippoolitem ' \
r'LEFT JOIN abonent ON abonent.ip_address_id = ip_pool_ippoolitem.id WHERE ' \
r'abonent.ip_address_id IS NULL LIMIT 1'
rs = self.raw(sql)
rs_len = len(list(rs))
return None if rs_len is 0 else rs[0]
class IpPoolItem(models.Model):
ip = MyGenericIPAddressField()
objects = IpPoolItemManager()
def int_ip(self):
return ip2int(self.ip)
def __unicode__(self):
return self.ip

3
ip_pool/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
ip_pool/urls.py

@ -0,0 +1,13 @@
# -*- coding:utf-8 -*-
from django.conf.urls import url
import views
urlpatterns = [
url(r'^$', views.home, name='pool_home_link'),
url(r'^range$', views.ips, name='pool_ips_link'),
url(r'^del$', views.del_pool, name='pool_ips_del_link'),
url(r'^add$', views.add_pool, name='pool_add_link'),
url(r'^delip$', views.delip, name='pool_del_ip_link')
]

76
ip_pool/views.py

@ -0,0 +1,76 @@
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect, get_object_or_404
from forms import PoolForm
from models import IpPoolItem
import mydefs
@login_required
def home(request):
pools = IpPoolItem.objects.get_pools()
print pools
if pools:
pools = map(lambda ip: (mydefs.int2ip(ip[0]), mydefs.int2ip(ip[1]), ip[2]), pools)
pools = mydefs.pag_mn(request, pools)
return render(request, 'ip_pool/index.html', {
'pools': pools
})
@login_required
def ips(request):
ip_start = request.GET.get('ips')
ip_end = request.GET.get('ipe')
pool_ips = IpPoolItem.objects.filter(ip__gte=ip_start)
pool_ips = pool_ips.filter(ip__lte=ip_end)
pool_ips = mydefs.pag_mn(request, pool_ips)
return render(request, 'ip_pool/ips.html', {
'pool_ips': pool_ips,
'ips': ip_start,
'ipe': ip_end
})
@login_required
def del_pool(request):
ip_start = request.GET.get('ips')
ip_end = request.GET.get('ipe')
pool_ips = IpPoolItem.objects.filter(ip__gte=ip_start)
pool_ips = pool_ips.filter(ip__lte=ip_end)
pool_ips = pool_ips.filter()
pool_ips.delete()
return mydefs.res_success(request, 'pool_home_link')
@login_required
def add_pool(request):
if request.method == 'POST':
frm = PoolForm(request.POST)
if frm.is_valid():
cd = frm.cleaned_data
IpPoolItem.objects.add_pool(cd['start_ip'], cd['end_ip'])
return redirect('pool_home_link')
else:
warntext = u'Form is not valid'
else:
frm = PoolForm()
warntext = ''
return render(request, 'ip_pool/add_pool.html', {
'form': frm,
'warntext': warntext
})
@login_required
def delip(request):
ipid = request.GET.get('id')
get_object_or_404(IpPoolItem, id=ipid).delete()
return mydefs.res_success(request, 'pool_home_link')

10
manage.py

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djing.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

0
mapapp/__init__.py

4
mapapp/admin.py

@ -0,0 +1,4 @@
from django.contrib import admin
import models
admin.site.register(models.Dot)

7
mapapp/apps.py

@ -0,0 +1,7 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class MapappConfig(AppConfig):
name = 'mapapp'

25
mapapp/migrations/0001_initial.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Dot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=64)),
('posX', models.FloatField()),
('posY', models.FloatField()),
],
),
]

0
mapapp/migrations/__init__.py

10
mapapp/models.py

@ -0,0 +1,10 @@
from django.db import models
class Dot(models.Model):
title = models.CharField(max_length=64)
posX = models.FloatField()
posY = models.FloatField()
def __unicode__(self):
return self.title

3
mapapp/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

7
mapapp/urls.py

@ -0,0 +1,7 @@
from django.conf.urls import url
import views
urlpatterns = [
url(r'^$', views.home, name='maps_home_link'),
url(r'^get_dots$', views.get_dots, name='maps_get_dots')
]

18
mapapp/views.py

@ -0,0 +1,18 @@
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import render
from models import Dot
from json import dumps
@login_required
def home(request):
return render(request, 'maps/index.html')
def get_dots(r):
dots = Dot.objects.all()
return HttpResponse(dumps({
'dots': map(lambda d: (d.id, d.posX, d.posY, d.title), dots)
}))

111
mydefs.py

@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
from django.http import HttpResponse, Http404
from json import dumps
from django.shortcuts import redirect
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
import socket
import struct
from django.db import models
from collections import Iterator
ip_addr_regex = r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
def ip2int(addr):
return struct.unpack("!I", socket.inet_aton(addr))[0]
def int2ip(addr):
return socket.inet_ntoa(struct.pack("!I", addr))
def safe_float(fl):
return 0.0 if fl is None or fl == '' else float(fl)
def safe_int(i):
return 0 if i is None or i == '' else int(i)
def res_success(request, redirect_to='/'):
if request.is_ajax():
return HttpResponse(dumps({'errnum': 0}))
else:
return redirect(redirect_to)
def res_error(request, text):
if request.is_ajax():
return HttpResponse(dumps({'errnum':1, 'errtext':text}))
else:
raise Http404(text)
# Pagination
def pag_mn(request, objs, count_per_page=10):
page = request.GET.get('p')
pgn = Paginator(objs, count_per_page)
try:
objs = pgn.page(page)
except PageNotAnInteger:
objs = pgn.page(1)
except EmptyPage:
objs = pgn.page(pgn.num_pages)
return objs
def context_processor_client_ipaddress(request):
ip = request.META.get('REMOTE_ADDR', '') or request.META.get('HTTP_X_FORWARDED_FOR', '')
return {
'client_ipaddress': ip
}
#logmodels.debug('%s %s %s'%(request.user, request.path, ip))
class MyGenericIPAddressField(models.GenericIPAddressField):
description = "Int32 notation ip address"
def __init__(self, protocol='IPv4', *args, **kwargs):
super(MyGenericIPAddressField, self).__init__(protocol=protocol, *args, **kwargs)
self.max_length = 8
def get_prep_value(self, value):
# strIp to Int
value = super(models.GenericIPAddressField, self).get_prep_value(value)
return ip2int(value)
def to_python(self, addr):
return addr
def get_internal_type(self):
return 'PositiveIntegerField'
@staticmethod
def from_db_value(value, expression, connection, context):
return int2ip(value)
# Предназначен для Django CHOICES чтоб можно было передавать классы вместо просто описания поля,
# классы передавать для того чтоб по значению кода из базы понять какой класс нужно взять для нужной функциональности.
# Например по коду в базе вам нужно определять как считать тариф абонента, что реализовано в возвращаемом классе.
class MyChoicesAdapter(Iterator):
chs = tuple()
current_index = 0
_max_index = 0
# На вход принимает кортеж кортежей, вложенный из 2х элементов: кода и класса, как: TARIFF_CHOICES
def __init__(self, choices):
self._max_index = len(choices)
self.chs = choices
def next(self):
if self.current_index >= self._max_index:
raise StopIteration
else:
e = self.chs
ci = self.current_index
res = e[ci][0], e[ci][1].description()
self.current_index += 1
return res

0
photo_app/__init__.py

4
photo_app/admin.py

@ -0,0 +1,4 @@
from django.contrib import admin
import models
admin.site.register(models.Photo)

25
photo_app/migrations/0001_initial.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Photo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(height_field=b'heigt', upload_to=b'', width_field=b'wdth')),
('wdth', models.PositiveSmallIntegerField(blank=True, default=b'759', editable=False, null=True)),
('heigt', models.PositiveSmallIntegerField(blank=True, editable=False, null=True)),
],
),
]

0
photo_app/migrations/__init__.py

69
photo_app/models.py

@ -0,0 +1,69 @@
# -*- coding:utf-8 -*-
from django.db import models
from djing.settings import BASE_DIR
import os
from PIL import Image
import time
import hashlib
class Photo(models.Model):
image = models.ImageField(width_field='wdth', height_field='heigt')
wdth = models.PositiveSmallIntegerField(null=True, blank=True, editable=False, default="759")
heigt = models.PositiveSmallIntegerField(null=True, blank=True, editable=False)
def __unicode__(self):
return "{0}".format(self.image)
def big(self):
return self.image.url
def min(self):
pth = self.image.url.split('/')[-1:][0]
return "/media/min/%s" % pth
def save(self, *args, **kwargs):
if not self.image:
return
super(Photo, self).save()
im = Image.open(self.image.path)
im.thumbnail((759, 759), Image.ANTIALIAS)
hs = hashlib.md5(str(time.time())).hexdigest()
ext = self.image.path.split('.')[1:][0]
fname = "%s/media/%s.%s" % (BASE_DIR, hs, ext)
im.save(fname)
os.remove(self.image.path)
self.image = "%s.%s" % (hs, ext)
super(Photo, self).save()
#class Meta:
# unique_together = (('image',),)
def resize_image(sender, instance, **kwargs):
if not kwargs['created']:
im = Image.open(instance.image.path)
im.thumbnail((200, 121), Image.ANTIALIAS)
pth = instance.image.path.split('/')[-1:][0]
fullpath = "%s/media/min/%s" % (BASE_DIR, pth)
im.save(fullpath)
def post_delete_photo(sender, instance, **kwargs):
min_fname = instance.image.path.split('/')[-1:][0]
try:
os.remove('%s/media/min/%s' % (BASE_DIR, min_fname))
os.remove(instance.image.path)
except OSError:
pass
models.signals.post_save.connect(resize_image, sender=Photo)
models.signals.post_delete.connect(post_delete_photo, sender=Photo)

16
photo_app/tests.py

@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

12
photo_app/urls.py

@ -0,0 +1,12 @@
# -*- coding:utf-8 -*-
from django.conf.urls import patterns, url
urlpatterns = [
#url(r'^$', 'photo_albums', name='media_home_link'),
#url(r'^(?P<id>\d+)$', 'photo_albums', name='media_home_link_by_id'),
#url(r'^album(?P<id>\d+)$', 'photos', name='media_photos_link'),
#url(r'^newalbum/$', 'add_album', name='add_media_album_link'),
#url(r'^newphoto/$', 'add_photo_to_album', name='add_media_photo_link')
]

1
photo_app/views.py

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

0
privatemessage/__init__.py

5
privatemessage/admin.py

@ -0,0 +1,5 @@
from django.contrib import admin
from models import PrivateMessages, Dialog
admin.site.register(PrivateMessages)
admin.site.register(Dialog)

7
privatemessage/context_processors.py

@ -0,0 +1,7 @@
from models import PrivateMessages
def avail_messages(request):
return {
'avail_messages_num': PrivateMessages.objects.get_my_messages(request)
}

39
privatemessage/migrations/0001_initial.py

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2016-06-28 23:51
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Dialog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=127)),
('date_create', models.DateTimeField(auto_now_add=True)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
('recepient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='PrivateMessages',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_send', models.DateTimeField(auto_now_add=True)),
('text', models.TextField()),
('is_viewed', models.BooleanField(default=False)),
('dialog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='privatemessage.Dialog')),
],
),
]

0
privatemessage/migrations/__init__.py

34
privatemessage/models.py

@ -0,0 +1,34 @@
from django.db import models
from djing import settings
class MessagesManager(models.Manager):
def get_my_messages(self, request):
if request.user.is_authenticated():
num = self.filter(recepient=request.user, is_viewed=False).count()
else:
num = 0
return int(num)
class Dialog(models.Model):
title = models.CharField(max_length=127)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
recepient = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
date_create = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
class PrivateMessages(models.Model):
dialog = models.ForeignKey(Dialog)
date_send = models.DateTimeField(auto_now_add=True)
text = models.TextField()
is_viewed = models.BooleanField(default=False)
objects = MessagesManager()
def __unicode__(self):
return self.text

22
privatemessage/tests.py

@ -0,0 +1,22 @@
from django.test import TestCase
import models
from django.contrib.auth.models import User
class PaysTest(TestCase):
def setUp(self):
self.msg = models.PrivateMessages.objects.create(
sender=User.objects.all()[0],
recepient=User.objects.all()[0],
text='test init text'
)
def tearDown(self):
models.PrivateMessages.objects.all().delete()
def check_ret_msgs(self):
"""check return messages"""
request = self.factory.get('/message/')
self.assertIsInstance(models.PrivateMessages.objects.get_my_messages(request), int, 'checking ret type')
self.assertGreater(models.PrivateMessages.objects.get_my_messages(request), 0, 'checking msg count')

8
privatemessage/urls.py

@ -0,0 +1,8 @@
from django.conf.urls import url
import views
urlpatterns = [
url(r'^$', views.home, name='privmsg_home'),
url(r'^delitem_(?P<id>\d+)$', views.delitem, name='privmsg_delitem'),
url(r'^write', views.send_message, name='privmsg_send_message')
]

54
privatemessage/views.py

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
from models import PrivateMessages
from django.http import HttpResponse
from json import dumps
from django.template.context_processors import csrf
import mydefs
from accounts_app.models import UserProfile
@login_required
def home(request):
msgs = PrivateMessages.objects.all()
return render(request, 'private_messages/index.html', {
'msgs': msgs
})
@login_required
def delitem(request, id=0):
r = {'errnum': 0,'errtext': u''}
try:
PrivateMessages.objects.get(id=id).delete()
except PrivateMessages.DoesNotExist:
r = {
'errnum': 1,
'errtext': u'Error while deleting, item does not exist'
}
return HttpResponse(dumps(r))
@login_required
def send_message(request):
if request.method == 'GET':
return HttpResponse(render_to_string('private_messages/send_form.html',{
'csrf_token': csrf(request)['csrf_token'],
'a': request.GET.get('a')
}))
elif request.method == 'POST':
try:
a = request.GET.get('a')
a = 0 if a is None or a == '' else int(a)
msg = PrivateMessages()
msg.sender = request.user
msg.recepient = UserProfile.objects.get(id=a)
msg.text = request.POST.get('msg_text')
msg.save()
return redirect('privmsg_home')
except UserProfile.DoesNotExist:
return mydefs.res_error(request, u'Адресат не найден')
else:
return mydefs.res_error(request, u'Ошибка типа запроса')

4
requirements.txt

@ -0,0 +1,4 @@
Django==1.9
requests
Pillow
MySQL-python

0
searchapp/__init__.py

3
searchapp/admin.py

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

7
searchapp/apps.py

@ -0,0 +1,7 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class SearchappConfig(AppConfig):
name = 'searchapp'

0
searchapp/migrations/__init__.py

5
searchapp/models.py

@ -0,0 +1,5 @@
from __future__ import unicode_literals
from django.db import models
# Create your models here.

3
searchapp/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

7
searchapp/urls.py

@ -0,0 +1,7 @@
from django.conf.urls import url
import views
urlpatterns = [
url(r'^$', views.home, name='search_home_link'),
]

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save