commit
2e888db44d
190 changed files with 6690 additions and 0 deletions
-
6.gitignore
-
65Doc.txt
-
0abonapp/__init__.py
-
8abonapp/admin.py
-
68abonapp/forms.py
-
110abonapp/migrations/0001_initial.py
-
0abonapp/migrations/__init__.py
-
349abonapp/models.py
-
50abonapp/tests.py
-
21abonapp/urls.py
-
19abonapp/urls_abon.py
-
439abonapp/views.py
-
0accounts_app/__init__.py
-
4accounts_app/admin.py
-
42accounts_app/migrations/0001_initial.py
-
0accounts_app/migrations/__init__.py
-
107accounts_app/models.py
-
16accounts_app/tests.py
-
27accounts_app/urls.py
-
258accounts_app/views.py
-
12agent.py
-
3agent/__init__.py
-
27agent/db.py
-
48agent/firewall.py
-
72agent/ipfw.sh
-
120agent/main.py
-
143agent/models.py
-
0agent/netflow/__init__.py
-
49agent/netflow/netflow_handler.py
-
11agent/netflow/netflow_handler.sh
-
7agent/netflow/start_netflow.sh
-
17agent/settings.py
-
226agent/sslTransmitter.py
-
1agent/tariff.py
-
11bugs.txt
-
18cron.py
-
0devapp/__init__.py
-
6devapp/admin.py
-
7devapp/apps.py
-
109devapp/base_intr.py
-
30devapp/dev_types.py
-
32devapp/forms.py
-
49devapp/migrations/0001_initial.py
-
0devapp/migrations/__init__.py
-
52devapp/models.py
-
3devapp/tests.py
-
10devapp/urls.py
-
59devapp/views.py
-
0djing/__init__.py
-
143djing/settings.py
-
26djing/urls.py
-
9djing/views.py
-
16djing/wsgi.py
-
17install.sql
-
1ip_pool/__init__.py
-
4ip_pool/admin.py
-
6ip_pool/apps.py
-
21ip_pool/forms.py
-
24ip_pool/migrations/0001_initial.py
-
0ip_pool/migrations/__init__.py
-
60ip_pool/models.py
-
3ip_pool/tests.py
-
13ip_pool/urls.py
-
76ip_pool/views.py
-
10manage.py
-
0mapapp/__init__.py
-
4mapapp/admin.py
-
7mapapp/apps.py
-
25mapapp/migrations/0001_initial.py
-
0mapapp/migrations/__init__.py
-
10mapapp/models.py
-
3mapapp/tests.py
-
7mapapp/urls.py
-
18mapapp/views.py
-
111mydefs.py
-
0photo_app/__init__.py
-
4photo_app/admin.py
-
25photo_app/migrations/0001_initial.py
-
0photo_app/migrations/__init__.py
-
69photo_app/models.py
-
16photo_app/tests.py
-
12photo_app/urls.py
-
1photo_app/views.py
-
0privatemessage/__init__.py
-
5privatemessage/admin.py
-
7privatemessage/context_processors.py
-
39privatemessage/migrations/0001_initial.py
-
0privatemessage/migrations/__init__.py
-
34privatemessage/models.py
-
22privatemessage/tests.py
-
8privatemessage/urls.py
-
54privatemessage/views.py
-
4requirements.txt
-
0searchapp/__init__.py
-
3searchapp/admin.py
-
7searchapp/apps.py
-
0searchapp/migrations/__init__.py
-
5searchapp/models.py
-
3searchapp/tests.py
-
7searchapp/urls.py
@ -0,0 +1,6 @@ |
|||||
|
*.pyc |
||||
|
*.db |
||||
|
media/* |
||||
|
media/min/* |
||||
|
~*/migrations/000*.py |
||||
|
.idea/ |
||||
@ -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,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) |
||||
@ -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'}) |
||||
|
) |
||||
@ -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,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) |
||||
@ -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) |
||||
@ -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) |
||||
|
|
||||
|
] |
||||
@ -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'), |
||||
|
] |
||||
@ -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,0 +1,4 @@ |
|||||
|
from django.contrib import admin |
||||
|
from models import UserProfile |
||||
|
|
||||
|
admin.site.register(UserProfile) |
||||
@ -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,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) |
||||
@ -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) |
||||
@ -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') |
||||
|
|
||||
|
] |
||||
@ -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 |
||||
|
}) |
||||
@ -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..." |
||||
@ -0,0 +1,3 @@ |
|||||
|
from models import Abonent, Tariff |
||||
|
from main import main |
||||
|
from sslTransmitter import get_TransmitterClientKlass, NetExcept |
||||
@ -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 |
||||
@ -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) |
||||
@ -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 |
||||
|
#---------------------- |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
# тут будем поджимать пользователей когда не хватает канала |
||||
@ -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() |
||||
@ -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,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() |
||||
@ -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 |
||||
@ -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 |
||||
@ -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 |
||||
@ -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() |
||||
@ -0,0 +1 @@ |
|||||
|
# -*- coding:utf-8 -*- |
||||
@ -0,0 +1,11 @@ |
|||||
|
- (GUI) Иконки возле кнопок не настроены, натыканы случайно |
||||
|
|
||||
|
Остановился на управлении купленными услугами(тарифами): |
||||
|
+ нужен порядок выполнения очереди тарифов (модель зделана) |
||||
|
+ Управление приоритетом тарифов (порядком выполнения) |
||||
|
- удаление ещё не активных купленных тарифов |
||||
|
+ отображение и выделение активного тарифа |
||||
|
|
||||
|
Нужно на каждое изменение данных об абоненте слать сигнал об этом на NAS |
||||
|
|
||||
|
Фаервол допилить, там с пайпами не понятно, не знаю принцип действия |
||||
@ -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,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) |
||||
@ -0,0 +1,7 @@ |
|||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class DevappConfig(AppConfig): |
||||
|
name = 'devapp' |
||||
@ -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) |
||||
@ -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' |
||||
|
} |
||||
@ -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' |
||||
|
}) |
||||
|
} |
||||
@ -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,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() |
||||
@ -0,0 +1,3 @@ |
|||||
|
from django.test import TestCase |
||||
|
|
||||
|
# Create your tests here. |
||||
@ -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'), |
||||
|
] |
||||
@ -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,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') |
||||
@ -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() |
||||
@ -0,0 +1,9 @@ |
|||||
|
from django.shortcuts import redirect |
||||
|
|
||||
|
|
||||
|
def home(request): |
||||
|
return redirect('profile') |
||||
|
|
||||
|
|
||||
|
def finance_report(request): |
||||
|
pass |
||||
@ -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() |
||||
@ -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) |
||||
@ -0,0 +1 @@ |
|||||
|
default_app_config = 'ip_pool.apps.IpPoolConfig' |
||||
@ -0,0 +1,4 @@ |
|||||
|
from django.contrib import admin |
||||
|
import models |
||||
|
|
||||
|
admin.site.register(models.IpPoolItem) |
||||
@ -0,0 +1,6 @@ |
|||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class IpPoolConfig(AppConfig): |
||||
|
name = 'ip_pool' |
||||
|
verbose_name = u"Ip pool" |
||||
@ -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) |
||||
@ -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,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 |
||||
@ -0,0 +1,3 @@ |
|||||
|
from django.test import TestCase |
||||
|
|
||||
|
# Create your tests here. |
||||
@ -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') |
||||
|
] |
||||
@ -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') |
||||
@ -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,0 +1,4 @@ |
|||||
|
from django.contrib import admin |
||||
|
import models |
||||
|
|
||||
|
admin.site.register(models.Dot) |
||||
@ -0,0 +1,7 @@ |
|||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class MapappConfig(AppConfig): |
||||
|
name = 'mapapp' |
||||
@ -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,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 |
||||
@ -0,0 +1,3 @@ |
|||||
|
from django.test import TestCase |
||||
|
|
||||
|
# Create your tests here. |
||||
@ -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') |
||||
|
] |
||||
@ -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) |
||||
|
})) |
||||
@ -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,0 +1,4 @@ |
|||||
|
from django.contrib import admin |
||||
|
import models |
||||
|
|
||||
|
admin.site.register(models.Photo) |
||||
@ -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,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) |
||||
@ -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) |
||||
@ -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') |
||||
|
] |
||||
@ -0,0 +1 @@ |
|||||
|
# -*- coding:utf-8 -*- |
||||
@ -0,0 +1,5 @@ |
|||||
|
from django.contrib import admin |
||||
|
from models import PrivateMessages, Dialog |
||||
|
|
||||
|
admin.site.register(PrivateMessages) |
||||
|
admin.site.register(Dialog) |
||||
@ -0,0 +1,7 @@ |
|||||
|
from models import PrivateMessages |
||||
|
|
||||
|
|
||||
|
def avail_messages(request): |
||||
|
return { |
||||
|
'avail_messages_num': PrivateMessages.objects.get_my_messages(request) |
||||
|
} |
||||
@ -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,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 |
||||
@ -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') |
||||
@ -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') |
||||
|
] |
||||
@ -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'Ошибка типа запроса') |
||||
@ -0,0 +1,4 @@ |
|||||
|
Django==1.9 |
||||
|
requests |
||||
|
Pillow |
||||
|
MySQL-python |
||||
@ -0,0 +1,3 @@ |
|||||
|
from django.contrib import admin |
||||
|
|
||||
|
# Register your models here. |
||||
@ -0,0 +1,7 @@ |
|||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.apps import AppConfig |
||||
|
|
||||
|
|
||||
|
class SearchappConfig(AppConfig): |
||||
|
name = 'searchapp' |
||||
@ -0,0 +1,5 @@ |
|||||
|
from __future__ import unicode_literals |
||||
|
|
||||
|
from django.db import models |
||||
|
|
||||
|
# Create your models here. |
||||
@ -0,0 +1,3 @@ |
|||||
|
from django.test import TestCase |
||||
|
|
||||
|
# Create your tests here. |
||||
@ -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
Write
Preview
Loading…
Cancel
Save
Reference in new issue