Browse Source

Merge branch 'devel' of https://github.com/nerosketch/djing into devel

devel
Dmitry Novikov 8 years ago
parent
commit
bf7c175c95
  1. 46
      abonapp/forms.py
  2. 23
      abonapp/locale/ru/LC_MESSAGES/django.po
  3. 41
      abonapp/models.py
  4. 14
      abonapp/templates/abonapp/modal_export.html
  5. 27
      abonapp/templates/abonapp/peoples.html
  6. 1
      abonapp/urls_abon.py
  7. 33
      abonapp/views.py
  8. 1
      accounts_app/models.py

46
abonapp/forms.py

@ -8,7 +8,6 @@ from string import digits, ascii_lowercase
from . import models
from django.conf import settings
TELEPHONE_REGEXP = getattr(settings, 'TELEPHONE_REGEXP', r'^\+[7,8,9,3]\d{10,11}$')
@ -16,7 +15,7 @@ def generate_random_username(length=6, chars=digits, split=2, delimiter=''):
username = ''.join([choice(chars) for i in range(length)])
if split:
username = delimiter.join([username[start:start+split] for start in range(0, len(username), split)])
username = delimiter.join([username[start:start + split] for start in range(0, len(username), split)])
try:
models.Abon.objects.get(username=username)
@ -26,7 +25,7 @@ def generate_random_username(length=6, chars=digits, split=2, delimiter=''):
def generate_random_password():
return generate_random_username(length=8, chars=digits+ascii_lowercase)
return generate_random_username(length=8, chars=digits + ascii_lowercase)
class AbonForm(forms.ModelForm):
@ -41,14 +40,16 @@ class AbonForm(forms.ModelForm):
if abon_group_queryset is not None:
self.fields['street'].queryset = abon_group_queryset
username = forms.CharField(max_length=127, required=False, initial=generate_random_username, widget=forms.TextInput(attrs={
'placeholder': _('login'),
'class': "form-control",
'required': ''
}))
username = forms.CharField(max_length=127, required=False, initial=generate_random_username,
widget=forms.TextInput(attrs={
'placeholder': _('login'),
'class': "form-control",
'required': ''
}))
password = forms.CharField(max_length=64, initial=generate_random_password,
widget=forms.TextInput(attrs={'class': 'form-control', 'type': 'password', 'autocomplete': 'new-password'}))
widget=forms.TextInput(
attrs={'class': 'form-control', 'type': 'password', 'autocomplete': 'new-password'}))
class Meta:
model = models.Abon
@ -116,7 +117,6 @@ class PassportForm(forms.ModelForm):
class ExtraFieldForm(forms.ModelForm):
class Meta:
model = models.ExtraFieldsModel
fields = '__all__'
@ -132,7 +132,7 @@ class AbonStreetForm(forms.ModelForm):
model = models.AbonStreet
fields = '__all__'
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control', 'required':'', 'autofocus':''}),
'name': forms.TextInput(attrs={'class': 'form-control', 'required': '', 'autofocus': ''}),
'group': forms.Select(attrs={'class': 'form-control'})
}
@ -148,5 +148,27 @@ class AdditionalTelephoneForm(forms.ModelForm):
'required': '',
'class': 'form-control'
}),
'owner_name': forms.TextInput(attrs={'class': 'form-control', 'required':''})
'owner_name': forms.TextInput(attrs={'class': 'form-control', 'required': ''})
}
class ExportUsersForm(forms.Form):
FIELDS_CHOICES = (
('username', _('profile username')),
('fio', _('fio')),
('ip_address', _('Ip Address')),
('description', _('Comment')),
('street', _('Street')),
('house', _('House')),
('birth_day', _('birth day')),
('is_active', _('Is active')),
('telephone', _('Telephone')),
('current_tariff', _('Service title')),
('ballance', _('Ballance')),
('device', _('Device')),
('dev_port', _('Device port')),
('is_dynamic_ip', _('Is dynamic ip'))
)
fields = forms.MultipleChoiceField(choices=FIELDS_CHOICES,
widget=forms.CheckboxSelectMultiple(attrs={"checked": ""}),
label=_('Fields'))

23
abonapp/locale/ru/LC_MESSAGES/django.po

@ -421,7 +421,7 @@ msgstr "Добавить муфту"
#: templates/abonapp/editAbon.html:171
msgid "Device port"
msgstr "Порт устройства"
msgstr "Порт устройства"
#: templates/abonapp/editAbon.html:190
msgid "Is dynamic network settings"
@ -995,3 +995,24 @@ msgstr "Показать график по дате"
#, python-format
msgid "<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this device"
msgstr "<a href='%(user_url)s'>%(user_name)s</a> уже привязан к этому порту на этом устройстве"
msgid "Export"
msgstr "Экспорт"
msgid "Export users"
msgstr "Экспорт абонентов"
msgid "Fields"
msgstr "Поля"
msgid "Select the fields"
msgstr "Выберите поля"
msgid "Service title"
msgstr "Название тарифа"
msgid "Is dynamic ip"
msgstr "Динамический ip"
msgid "Unexpected format %(export_format)s"
msgstr "Нежиданный формат %(export_format)s"

41
abonapp/models.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
@ -55,23 +54,21 @@ class AbonLog(models.Model):
class AbonTariff(models.Model):
tariff = models.ForeignKey(Tariff, models.CASCADE, related_name='linkto_tariff')
# время начала действия услуги
time_start = models.DateTimeField(null=True, blank=True, default=None)
# время завершения услуги
deadline = models.DateTimeField(null=True, blank=True, default=None)
def calc_amount_service(self):
amount = self.tariff.amount
return round(amount, 2)
# Используется-ли услуга сейчас, если время старта есть то он активирован
# is used service now, if time start is present than it activated
def is_started(self):
return False if self.time_start is None else True
def __str__(self):
return "%d: %s" % (
self.pk or 0,
return "%s: %s" % (
self.deadline,
self.tariff.title
)
@ -139,7 +136,6 @@ class ExtraFieldsModel(models.Model):
db_table = 'abon_extra_fields'
class AbonManager(MyUserManager):
def get_queryset(self):
@ -159,7 +155,6 @@ class Abon(UserProfile):
dev_port = models.ForeignKey('devapp.Port', null=True, blank=True, on_delete=models.SET_NULL)
is_dynamic_ip = models.BooleanField(default=False)
# возвращает связь с текущим тарифом для абонента
def active_tariff(self):
return self.current_tariff
@ -177,12 +172,10 @@ class Abon(UserProfile):
verbose_name = _('Abon')
verbose_name_plural = _('Abons')
# Платим за что-то
def make_pay(self, curuser, how_match_to_pay=0.0):
self.ballance -= how_match_to_pay
self.save(update_fields=['ballance'])
# Пополняем счёт
def add_ballance(self, current_user, amount, comment):
AbonLog.objects.create(
abon=self,
@ -192,7 +185,6 @@ class Abon(UserProfile):
)
self.ballance += amount
# покупаем тариф
def pick_tariff(self, tariff, author, comment=None, deadline=None):
if not isinstance(tariff, Tariff):
raise TypeError
@ -204,13 +196,13 @@ class Abon(UserProfile):
if self.current_tariff is not None:
if self.current_tariff.tariff == tariff:
# Эта услуга уже подключена
# if service already connected
raise LogicError(_('That service already activated'))
else:
# Не надо молча заменять услугу если какая-то уже есть
# if service is present then speak about it
raise LogicError(_('Service already activated'))
# если не хватает денег
# if not enough money
if self.ballance < amount:
raise LogicError(_('not enough money'))
@ -218,30 +210,30 @@ class Abon(UserProfile):
new_abtar.save()
self.current_tariff = new_abtar
# снимаем деньги за услугу
# charge for the service
self.ballance -= amount
self.save()
# Запись об этом в лог
# make log about it
AbonLog.objects.create(
abon=self, amount=-tariff.amount,
author=author,
comment=comment or _('Buy service default log')
)
# Производим расчёт услуги абонента, т.е. завершаем если пришло время
# Destroy the service if the time has come
def bill_service(self, author):
abon_tariff = self.active_tariff()
if abon_tariff is None:
return
nw = timezone.now()
# если услуга просрочена
# if service is overdue
if nw > abon_tariff.deadline:
print("Service %s for user %s is overdued, end service" % (abon_tariff.tariff, self))
abon_tariff.delete()
# есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access()
# is subscriber have access to service, view in tariff_app.custom_tariffs.<TariffBase>.manage_access()
def is_access(self):
abon_tariff = self.active_tariff()
if abon_tariff is None:
@ -250,7 +242,7 @@ class Abon(UserProfile):
ct = trf.get_calc_type()(abon_tariff)
return ct.manage_access(self)
# создаём абонента из структуры агента
# make subscriber from agent structure
def build_agent_struct(self):
if self.ip_address:
user_ip = ip2int(self.ip_address)
@ -265,7 +257,7 @@ class Abon(UserProfile):
return AbonStruct(self.pk, user_ip, agent_trf, bool(self.is_active))
def save(self, *args, **kwargs):
# проверяем не-ли у кого такого-же ip
# check if ip address already busy
if self.ip_address is not None and Abon.objects.filter(ip_address=self.ip_address).exclude(pk=self.pk).count() > 0:
self.is_bad_ip = True
raise LogicError(_('Ip address already exist'))
@ -319,7 +311,8 @@ class InvoiceForPayment(models.Model):
class AllTimePayLogManager(models.Manager):
def by_days(self):
@staticmethod
def by_days():
cur = connection.cursor()
cur.execute(r'SELECT SUM(summ) as alsum, DATE_FORMAT(date_add, "%Y-%m-%d") AS pay_date FROM all_time_pay_log '
r'GROUP BY DATE_FORMAT(date_add, "%Y-%m-%d")')
@ -408,10 +401,8 @@ def abon_post_save(sender, **kwargs):
try:
tm = Transmitter()
if created:
# создаём абонента
tm.add_user(agent_abon, ip_timeout=timeout)
else:
# обновляем абонента на NAS
tm.update_user(agent_abon, ip_timeout=timeout)
except (NasFailedResult, NasNetworkError, ConnectionResetError) as e:
@ -426,9 +417,7 @@ def abon_del_signal(sender, **kwargs):
ab = abon.build_agent_struct()
if ab is None:
return True
# подключаемся к NAS'у
tm = Transmitter()
# нашли абонента, и удаляем его на NAS
tm.remove_user(ab)
except (NasFailedResult, NasNetworkError):
return True

14
abonapp/templates/abonapp/modal_export.html

@ -0,0 +1,14 @@
{% load i18n %}
{% load bootstrap3 %}
<form role="form" action="{% url 'abonapp:abon_export' gid %}?f=csv" method="post"> {% csrf_token %}
<div class="modal-header primary">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title"><span class="glyphicon glyphicon-plus"></span>{% trans 'Select the fields' %}</h4>
</div>
<div class="modal-body">
{% bootstrap_form form %}
<button type="submit" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-export"></span> {% trans 'Export' %}
</button>
</div>
</form>

27
abonapp/templates/abonapp/peoples.html

@ -3,14 +3,13 @@
{% load dpagination %}
{% block main %}
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'abonapp:group_list' %}">{% trans 'User groups' %}</a></li>
<li class="active">{{ abon_group.title }}</li>
</ol>
<ol class="breadcrumb">
<li><span class="glyphicon glyphicon-home"></span></li>
<li><a href="{% url 'abonapp:group_list' %}">{% trans 'User groups' %}</a></li>
<li class="active">{{ abon_group.title }}</li>
</ol>
{% include 'message_block.html' %}
{% include 'message_block.html' %}
<h3>{% trans 'The people in the selected group' %}</h3>
<div class="row">
@ -21,32 +20,32 @@
<tr>
<th>#</th>
<th class="col-xs-1">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='username' dir=dir|default:"down" %}">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='username' dir=dir|default:'down' %}">
{% trans 'Sub' %}
</a>
{% if order_by == 'username' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="hidden-xs">{% trans 'Last traffic' %}</th>
<th class="col-xs-1 hidden-md">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='ip_address' dir=dir|default:"down" %}">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='ip_address' dir=dir|default:'down' %}">
{% trans 'Ip address' %}
</a>
{% if order_by == 'ip_address' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="col-xs-2">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='fio' dir=dir|default:"down" %}">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='fio' dir=dir|default:'down' %}">
{% trans 'fio' %}
</a>
{% if order_by == 'fio' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="col-xs-2">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='street' dir=dir|default:"down" %}">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='street' dir=dir|default:'down' %}">
{% trans 'Street' %}
</a>
{% if order_by == 'street' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
</th>
<th class="col-xs-1">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='house' dir=dir|default:"down" %}">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='house' dir=dir|default:'down' %}">
{% trans 'Apartment' %}
</a>
{% if order_by == 'house' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
@ -54,7 +53,7 @@
<th class="col-xs-2">{% trans 'Telephone' %}</th>
<th class="col-xs-2">{% trans 'Service' %}</th>
<th class="hidden-xs col-sm-1">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='ballance' dir=dir|default:"down" %}">
<a href="{% url 'abonapp:people_list' abon_group.pk %}?{% url_replace request order_by='ballance' dir=dir|default:'down' %}">
{% trans 'Ballance' %}
</a>
{% if order_by == 'ballance' %}<span class="glyphicon glyphicon-filter"></span>{% endif %}
@ -165,6 +164,6 @@
</div>
</div>
{% include 'toolbar_page.html' with pag=peoples %}
{% include 'toolbar_page.html' with pag=peoples %}
{% endblock %}

1
abonapp/urls_abon.py

@ -7,6 +7,7 @@ urlpatterns = [
url(r'^addabon$', views.addabon, name='add_abon'),
url(r'^services$', views.chgroup_tariff, name='ch_group_tariff'),
url(r'^phonebook$', views.phonebook, name='phonebook'),
url(r'^export$', views.abon_export, name='abon_export'),
url(r'^street/add$', views.street_add, name='street_add'),
url(r'^street/edit', views.street_edit, name='street_edit'),
url(r'^street/(?P<sid>\d+)/delete$', views.street_del, name='street_del'),

33
abonapp/views.py

@ -907,6 +907,39 @@ def phonebook(request, gid):
}, request=request)
@login_required
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))
def abon_export(request, gid):
res_format = request.GET.get('f')
if request.method == 'POST':
frm = forms.ExportUsersForm(request.POST)
if frm.is_valid():
cleaned_data = frm.clean()
fields = cleaned_data.get('fields')
subscribers = models.Abon.objects.filter(group__id=gid).only(*fields).values_list(*fields)
if res_format == 'csv':
import csv
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="users.csv"'
writer = csv.writer(response, quoting=csv.QUOTE_NONNUMERIC)
for row in subscribers:
writer.writerow(row)
return response
else:
messages.info(request, _('Unexpected format %(export_format)s') % {'export_format': res_format})
return redirect('abonapp:group_list')
else:
messages.error(request, _('fix form errors'))
return redirect('abonapp:group_list')
else:
frm = forms.ExportUsersForm()
return render_to_text('abonapp/modal_export.html', {
'gid': gid,
'form': frm
}, request=request)
@login_required
@permission_required('abonapp.change_abon')
@permission_required('abonapp.can_view_abongroup', (models.AbonGroup, 'pk', 'gid'))

1
accounts_app/models.py

@ -108,3 +108,4 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
)
verbose_name = _('Staff account profile')
verbose_name_plural = _('Staff account profiles')
ordering = ['fio']
Loading…
Cancel
Save