29 changed files with 1 additions and 1076 deletions
-
40abonapp/locale/ru/LC_MESSAGES/django.po
-
45abonapp/templates/abonapp/dial_log.html
-
10abonapp/templates/abonapp/editAbon.html
-
5abonapp/templates/abonapp/ext.htm
-
1abonapp/urls.py
-
55abonapp/views.py
-
0dialing_app/__init__.py
-
3dialing_app/admin.py
-
5dialing_app/apps.py
-
8dialing_app/forms.py
-
172dialing_app/locale/ru/LC_MESSAGES/django.po
-
81dialing_app/migrations/0001_initial.py
-
27dialing_app/migrations/0002_auto_20180808_1236.py
-
0dialing_app/migrations/__init__.py
-
109dialing_app/models.py
-
58dialing_app/templates/ext.html
-
37dialing_app/templates/inbox_sms.html
-
78dialing_app/templates/index.html
-
23dialing_app/templates/modal_send_sms.html
-
48dialing_app/templates/vmail.html
-
0dialing_app/templatetags/__init__.py
-
20dialing_app/templatetags/telephone_filters.py
-
3dialing_app/tests.py
-
15dialing_app/urls.py
-
221dialing_app/views.py
-
1djing/settings.py
-
1djing/urls.py
-
2docs/install.md
-
9templates/base.html
@ -1,45 +0,0 @@ |
|||
{% extends request.is_ajax|yesno:'nullcont.htm,abonapp/ext.htm' %} |
|||
{% load i18n %} |
|||
{% block content %} |
|||
|
|||
<table class="table table-striped table-bordered"> |
|||
<thead> |
|||
<tr> |
|||
<th>{% trans 'Play' %}</th> |
|||
<th>{% trans 'calldate' %}</th> |
|||
<th>{% trans 'src' %}</th> |
|||
<th>{% trans 'dst' %}</th> |
|||
<th>{% trans 'duration' %}</th> |
|||
<th>{% trans 'start' %}</th> |
|||
<th>{% trans 'answer' %}</th> |
|||
<th>{% trans 'end' %}</th> |
|||
<th>{% trans 'disposition' %}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% for log in logs %} |
|||
<tr> |
|||
<td> |
|||
<audio preload="metadata" controls> |
|||
<source src="{{ log.url }}" type="audio/wav"/> |
|||
</audio> |
|||
</td> |
|||
<td>{{ log.calldate|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ log.src }}</td> |
|||
<td>{{ log.dst }}</td> |
|||
<td>{{ log.duration }}</td> |
|||
<td>{{ log.start|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ log.answer|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ log.end|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ log.locate_disposition }}</td> |
|||
</tr> |
|||
{% empty %} |
|||
<tr> |
|||
<td colspan="9">{% trans 'Calls was not found' %}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</tbody> |
|||
|
|||
</table> |
|||
|
|||
{% endblock %} |
|||
@ -1,3 +0,0 @@ |
|||
from django.contrib import admin |
|||
|
|||
# Register your models here. |
|||
@ -1,5 +0,0 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class DialingAppConfig(AppConfig): |
|||
name = 'dialing_app' |
|||
@ -1,8 +0,0 @@ |
|||
from django import forms |
|||
from .models import SMSOut |
|||
|
|||
|
|||
class SMSOutForm(forms.ModelForm): |
|||
class Meta: |
|||
model = SMSOut |
|||
fields = ('dst', 'text') |
|||
@ -1,172 +0,0 @@ |
|||
# SOME DESCRIPTIVE TITLE. |
|||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
|||
# This file is distributed under the same license as the PACKAGE package. |
|||
# Dmitry Novikov nerosketch@gmail.com, 2017. |
|||
# |
|||
#, fuzzy |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: PACKAGE VERSION\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2018-08-09 14:59+0300\n" |
|||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
|||
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" |
|||
"Language: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: 8bit\n" |
|||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" |
|||
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" |
|||
"%100>=11 && n%100<=14)? 2 : 3);\n" |
|||
|
|||
#: models.py:9 models.py:44 |
|||
msgid "No answer" |
|||
msgstr "Не отвечен" |
|||
|
|||
#: models.py:10 models.py:46 models.py:101 |
|||
msgid "Failed" |
|||
msgstr "С ошибкой" |
|||
|
|||
#: models.py:11 models.py:48 |
|||
msgid "Busy" |
|||
msgstr "Занято" |
|||
|
|||
#: models.py:12 models.py:50 |
|||
msgid "Answered" |
|||
msgstr "Отвечен" |
|||
|
|||
#: models.py:13 models.py:52 |
|||
msgid "Unknown" |
|||
msgstr "Не определён" |
|||
|
|||
#: models.py:86 models.py:87 |
|||
msgid "SMS" |
|||
msgstr "СМС" |
|||
|
|||
#: models.py:95 |
|||
msgid "When" |
|||
msgstr "Когда" |
|||
|
|||
#: models.py:96 templates/index.html:25 |
|||
msgid "Telephone" |
|||
msgstr "Телефон" |
|||
|
|||
#: models.py:97 |
|||
msgid "Text" |
|||
msgstr "Текст" |
|||
|
|||
#: models.py:99 |
|||
msgid "New" |
|||
msgstr "Новое" |
|||
|
|||
#: models.py:100 |
|||
msgid "Sent" |
|||
msgstr "Отправленное" |
|||
|
|||
#: models.py:103 |
|||
msgid "Status" |
|||
msgstr "Статус" |
|||
|
|||
#: models.py:111 models.py:112 |
|||
msgid "Out SMS" |
|||
msgstr "Исходящие СМС" |
|||
|
|||
#: templates/ext.html:8 templates/ext.html:25 views.py:45 |
|||
msgid "Last calls" |
|||
msgstr "Последние звонки" |
|||
|
|||
#: templates/ext.html:32 views.py:74 |
|||
msgid "Voice mail request" |
|||
msgstr "Заявки на подключение" |
|||
|
|||
#: templates/ext.html:39 views.py:83 |
|||
msgid "Voice mail report" |
|||
msgstr "Заявки на поломки" |
|||
|
|||
#: templates/ext.html:46 templates/inbox_sms.html:7 |
|||
msgid "Inbox sms" |
|||
msgstr "Входящие смс" |
|||
|
|||
#: templates/inbox_sms.html:23 |
|||
msgid "Message history is empty" |
|||
msgstr "История сообщений пуста" |
|||
|
|||
#: templates/inbox_sms.html:31 templates/modal_send_sms.html:5 |
|||
msgid "Send sms" |
|||
msgstr "Отправить смс" |
|||
|
|||
#: templates/index.html:9 templates/vmail.html:10 |
|||
msgid "Play" |
|||
msgstr "Слушать" |
|||
|
|||
#: templates/index.html:10 templates/index.html:36 templates/vmail.html:11 |
|||
msgid "calldate" |
|||
msgstr "дата звонка" |
|||
|
|||
#: templates/index.html:11 templates/vmail.html:12 |
|||
msgid "src" |
|||
msgstr "кто" |
|||
|
|||
#: templates/index.html:12 |
|||
msgid "dst" |
|||
msgstr "куда" |
|||
|
|||
#: templates/index.html:13 templates/vmail.html:13 |
|||
msgid "duration" |
|||
msgstr "продолжительность" |
|||
|
|||
#: templates/index.html:14 templates/vmail.html:14 |
|||
msgid "start" |
|||
msgstr "начало" |
|||
|
|||
#: templates/index.html:15 templates/vmail.html:15 |
|||
msgid "answer" |
|||
msgstr "ответ" |
|||
|
|||
#: templates/index.html:16 templates/vmail.html:16 |
|||
msgid "end" |
|||
msgstr "конец" |
|||
|
|||
#: templates/index.html:17 templates/vmail.html:17 |
|||
msgid "disposition" |
|||
msgstr "состояние" |
|||
|
|||
#: templates/index.html:23 views.py:94 |
|||
msgid "Find dials" |
|||
msgstr "Найти звонки" |
|||
|
|||
#: templates/index.html:34 |
|||
msgid "Find by dates" |
|||
msgstr "Найти по дате" |
|||
|
|||
#: templates/index.html:64 |
|||
msgid "Download" |
|||
msgstr "Скачать" |
|||
|
|||
#: templates/index.html:80 templates/vmail.html:42 |
|||
msgid "Calls was not found" |
|||
msgstr "Звонки не найдены" |
|||
|
|||
#: templates/modal_send_sms.html:19 |
|||
msgid "Send" |
|||
msgstr "Отправить" |
|||
|
|||
#: views.py:55 |
|||
msgid "Multiple users with the telephone number" |
|||
msgstr "Несколько абонентов с указанным номером телефона" |
|||
|
|||
#: views.py:57 |
|||
msgid "User with the telephone number not found" |
|||
msgstr "Абонент с таким номером телефона не найден" |
|||
|
|||
#: views.py:114 |
|||
msgid "Make sure that your date format is correct" |
|||
msgstr "Убедитесь что формат времени корректен" |
|||
|
|||
#: views.py:138 |
|||
msgid "Message was enqueued for sending" |
|||
msgstr "Сообщение было поставлено в очередь для отправки" |
|||
|
|||
#: views.py:151 |
|||
msgid "fix form errors" |
|||
msgstr "Некоторые поля заполнены не правильно, проверте ещё раз" |
|||
@ -1,81 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.11 on 2018-02-26 00:20 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='AsteriskCDR', |
|||
fields=[ |
|||
('calldate', models.DateTimeField(default='0000-00-00 00:00:00', primary_key=True, serialize=False)), |
|||
('clid', models.CharField(default='', max_length=80)), |
|||
('src', models.CharField(default='', max_length=80)), |
|||
('dst', models.CharField(default='', max_length=80)), |
|||
('dcontext', models.CharField(default='', max_length=80)), |
|||
('channel', models.CharField(default='', max_length=80)), |
|||
('dstchannel', models.CharField(default='', max_length=80)), |
|||
('lastapp', models.CharField(default='', max_length=80)), |
|||
('lastdata', models.CharField(default='', max_length=80)), |
|||
('duration', models.IntegerField(default=0)), |
|||
('billsec', models.IntegerField(default=0)), |
|||
('start', models.DateTimeField(blank=True, default=None, null=True)), |
|||
('answer', models.DateTimeField(blank=True, default=None, null=True)), |
|||
('end', models.DateTimeField(blank=True, default=None, null=True)), |
|||
('disposition', models.CharField( |
|||
choices=[('NO ANSWER', 'No answer'), ('FAILED', 'Failed'), ('BUSY', 'Busy'), |
|||
('ANSWERED', 'Answered'), ('UNKNOWN', 'Unknown')], default='', max_length=45)), |
|||
('amaflags', models.IntegerField(default=0)), |
|||
('accountcode', models.CharField(default='', max_length=20)), |
|||
('userfield', models.CharField(default='', max_length=255)), |
|||
('uniqueid', models.CharField(default='', max_length=32)), |
|||
], |
|||
options={ |
|||
'db_table': 'cdr', |
|||
'ordering': ['-calldate'], |
|||
'managed': False, |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='SMSModel', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('when', models.DateTimeField(auto_now_add=True)), |
|||
('who', models.CharField(max_length=32)), |
|||
('dev', models.CharField(max_length=20)), |
|||
('text', models.CharField(max_length=255)), |
|||
], |
|||
options={ |
|||
'verbose_name': 'SMS', |
|||
'verbose_name_plural': 'SMS', |
|||
'db_table': 'sms', |
|||
'ordering': ['-when'], |
|||
'permissions': (('can_view_sms', 'Can view sms'),), |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='SMSOut', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('when', models.DateTimeField(auto_now_add=True, verbose_name='When')), |
|||
('dst', models.CharField(max_length=16, verbose_name='Telephone')), |
|||
('text', models.CharField(max_length=255, verbose_name='Text')), |
|||
('status', |
|||
models.CharField(choices=[('nw', 'New'), ('st', 'Sent'), ('fd', 'Failed')], default='nw', max_length=2, |
|||
verbose_name='Status')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Out SMS', |
|||
'verbose_name_plural': 'Out SMS', |
|||
'db_table': 'out_sms', |
|||
'permissions': (('can_view_sms', 'Can view sms'), ('can_send_sms', 'Can send sms')), |
|||
}, |
|||
), |
|||
] |
|||
@ -1,27 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.11 on 2018-08-08 12:36 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('dialing_app', '0001_initial'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name='asteriskcdr', |
|||
options={'managed': False, 'ordering': ('-calldate',)}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='smsmodel', |
|||
options={'ordering': ('-when',), 'permissions': (('can_view_sms', 'Can view sms'),), 'verbose_name': 'SMS', 'verbose_name_plural': 'SMS'}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='smsout', |
|||
options={'ordering': ('-when',), 'permissions': (('can_view_sms', 'Can view sms'), ('can_send_sms', 'Can send sms')), 'verbose_name': 'Out SMS', 'verbose_name_plural': 'Out SMS'}, |
|||
), |
|||
] |
|||
@ -1,109 +0,0 @@ |
|||
from django.db import models |
|||
from datetime import datetime |
|||
from django.utils.translation import gettext_lazy as _ |
|||
from django.conf import settings |
|||
|
|||
|
|||
class AsteriskCDR(models.Model): |
|||
DISPOSITION_CHOICES = ( |
|||
('NO ANSWER', _('No answer')), |
|||
('FAILED', _('Failed')), |
|||
('BUSY', _('Busy')), |
|||
('ANSWERED', _('Answered')), |
|||
('UNKNOWN', _('Unknown')) |
|||
) |
|||
calldate = models.DateTimeField(default='0000-00-00 00:00:00', primary_key=True) |
|||
clid = models.CharField(max_length=80, default='') |
|||
src = models.CharField(max_length=80, default='') |
|||
dst = models.CharField(max_length=80, default='') |
|||
dcontext = models.CharField(max_length=80, default='') |
|||
channel = models.CharField(max_length=80, default='') |
|||
dstchannel = models.CharField(max_length=80, default='') |
|||
lastapp = models.CharField(max_length=80, default='') |
|||
lastdata = models.CharField(max_length=80, default='') |
|||
duration = models.IntegerField(default=0) |
|||
billsec = models.IntegerField(default=0) |
|||
start = models.DateTimeField(null=True, blank=True, default=None) |
|||
answer = models.DateTimeField(null=True, blank=True, default=None) |
|||
end = models.DateTimeField(null=True, blank=True, default=None) |
|||
disposition = models.CharField(max_length=45, choices=DISPOSITION_CHOICES, default='') |
|||
amaflags = models.IntegerField(default=0) |
|||
accountcode = models.CharField(max_length=20, default='') |
|||
userfield = models.CharField(max_length=255, default='') |
|||
uniqueid = models.CharField(max_length=32, default='') |
|||
|
|||
def save(self, *args, **kwargs): |
|||
return |
|||
|
|||
def delete(self, *args, **kwargs): |
|||
return |
|||
|
|||
def locate_disposition(self): |
|||
dsp = self.disposition |
|||
if dsp == 'NO ANSWER': |
|||
return _('No answer') |
|||
elif dsp == 'FAILED': |
|||
return _('Failed') |
|||
elif dsp == 'BUSY': |
|||
return _('Busy') |
|||
elif dsp == 'ANSWERED': |
|||
return _('Answered') |
|||
elif dsp == 'UNKNOWN': |
|||
return _('Unknown') |
|||
return '' |
|||
|
|||
def path_to_media(self): |
|||
path = getattr(settings, 'DIALING_MEDIA', '/media') |
|||
if self.userfield == 'request': |
|||
return "%s/recording/request" % path |
|||
elif self.userfield == 'report': |
|||
return "%s/recording/bug" % path |
|||
return "%s/monitor" % path |
|||
|
|||
def url(self): |
|||
if type(self.calldate) is datetime: |
|||
return "%s/%s-%s-%s.wav" % ( |
|||
self.path_to_media(), self.calldate.strftime('%Y/%m/%d/%H_%M'), self.src, self.dst |
|||
) |
|||
|
|||
class Meta: |
|||
db_table = 'cdr' |
|||
managed = False |
|||
ordering = ('-calldate',) |
|||
|
|||
|
|||
class SMSModel(models.Model): |
|||
when = models.DateTimeField(auto_now_add=True) |
|||
who = models.CharField(max_length=32) |
|||
dev = models.CharField(max_length=20) |
|||
text = models.CharField(max_length=255) |
|||
|
|||
class Meta: |
|||
db_table = 'sms' |
|||
verbose_name = _('SMS') |
|||
verbose_name_plural = _('SMS') |
|||
ordering = ('-when',) |
|||
|
|||
def __str__(self): |
|||
return self.text |
|||
|
|||
|
|||
class SMSOut(models.Model): |
|||
when = models.DateTimeField(_('When'), auto_now_add=True) |
|||
dst = models.CharField(_('Telephone'), max_length=16) |
|||
text = models.CharField(_('Text'), max_length=255) |
|||
SMS_OUT_STATUS = ( |
|||
('nw', _('New')), |
|||
('st', _('Sent')), |
|||
('fd', _('Failed')) |
|||
) |
|||
status = models.CharField(_('Status'), max_length=2, choices=SMS_OUT_STATUS, default='nw') |
|||
|
|||
class Meta: |
|||
db_table = 'out_sms' |
|||
verbose_name = _('Out SMS') |
|||
verbose_name_plural = _('Out SMS') |
|||
ordering = ('-when',) |
|||
|
|||
def __str__(self): |
|||
return self.text |
|||
@ -1,58 +0,0 @@ |
|||
{% extends request.is_ajax|yesno:'bajax.html,base.html' %} |
|||
{% load i18n %} |
|||
|
|||
|
|||
{% block breadcrumb %} |
|||
<ol class="breadcrumb"> |
|||
<li><span class="glyphicon glyphicon-home"></span></li> |
|||
<li class="active">{% trans 'Last calls' %}</li> |
|||
</ol> |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block page-header %} |
|||
{{ title }} |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block main %} |
|||
|
|||
<ul class="nav nav-tabs"> |
|||
|
|||
{% url 'dialapp:home' as dialhome %} |
|||
<li{% if dialhome == request.path %} class="active"{% endif %}> |
|||
<a href="{{ dialhome }}"> |
|||
{% trans 'Last calls' %} |
|||
</a> |
|||
</li> |
|||
|
|||
{% url 'dialapp:vmail_request' as dialmail %} |
|||
<li{% if dialmail == request.path %} class="active"{% endif %}> |
|||
<a href="{{ dialmail }}"> |
|||
{% trans 'Voice mail request' %} |
|||
</a> |
|||
</li> |
|||
|
|||
{% url 'dialapp:vmail_report' as dialmail %} |
|||
<li{% if dialmail == request.path %} class="active"{% endif %}> |
|||
<a href="{{ dialmail }}"> |
|||
{% trans 'Voice mail report' %} |
|||
</a> |
|||
</li> |
|||
|
|||
{% url 'dialapp:inbox_sms' as dialsmsin %} |
|||
<li{% if dialsmsin == request.path %} class="active"{% endif %}> |
|||
<a href="{{ dialsmsin }}"> |
|||
{% trans 'Inbox sms' %} |
|||
</a> |
|||
</li> |
|||
|
|||
</ul> |
|||
|
|||
<div class="tab-content"> |
|||
<div class="tab-pane active"> |
|||
{% block content %}{% endblock %} |
|||
</div> |
|||
</div> |
|||
|
|||
{% endblock %} |
|||
@ -1,37 +0,0 @@ |
|||
{% extends request.is_ajax|yesno:'nullcont.htm,ext.html' %} |
|||
{% load i18n %} |
|||
{% block content %} |
|||
|
|||
<div class="panel panel-default"> |
|||
<div class="panel-heading"> |
|||
<h3 class="panel-title">{% trans 'Inbox sms' %}</h3> |
|||
</div> |
|||
<div class="list-group scroll-area"> |
|||
|
|||
{% for msg in sms_messages %} |
|||
|
|||
<div class="list-group-item"> |
|||
<h5>From {{ msg.who }} |
|||
<small>{{ msg.when|date:'d M, H:i:s' }} via {{ msg.dev }}</small> |
|||
</h5> |
|||
|
|||
<pre>{{ msg.text }}</pre> |
|||
</div> |
|||
|
|||
{% empty %} |
|||
<div class="list-group-item"> |
|||
<h4 class="list-group-item-heading">{% trans 'Message history is empty' %}</h4> |
|||
</div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
<div class="panel-footer"> |
|||
{% if perms.dialing_app.add_smsout %} |
|||
<a href="{% url 'dialapp:send_sms' %}" class="btn btn-default btn-modal"> |
|||
<span class="glyphicon glyphicon-envelope"></span> {% trans 'Send sms' %} |
|||
</a> |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
|
|||
{% endblock %} |
|||
@ -1,78 +0,0 @@ |
|||
{% extends request.is_ajax|yesno:'nullcont.htm,ext.html' %} |
|||
{% load i18n telephone_filters %} |
|||
{% block content %} |
|||
<div class="table-responsive"> |
|||
<table class="table table-striped table-bordered"> |
|||
<thead> |
|||
<tr> |
|||
<th>{% trans 'Play' %}</th> |
|||
<th>{% trans 'calldate' %}</th> |
|||
<th>{% trans 'src' %}</th> |
|||
<th>{% trans 'dst' %}</th> |
|||
<th>{% trans 'duration' %}</th> |
|||
<th>{% trans 'start' %}</th> |
|||
<th>{% trans 'answer' %}</th> |
|||
<th>{% trans 'end' %}</th> |
|||
<th>{% trans 'disposition' %}</th> |
|||
</tr> |
|||
<tr> |
|||
<th colspan="9"> |
|||
<form class="form-inline" action="{% url 'dialapp:vfilter' %}" method="get"> |
|||
<div class="form-group"> |
|||
<label class="sr-only" for="dialsearch">{% trans 'Find dials' %}</label> |
|||
<div class="input-group input-group-sm"> |
|||
<input type="text" class="form-control" id="dialsearch" placeholder="{% trans 'Telephone' %}" name="s"{% if s %} value="{{ s }}"{% endif %}> |
|||
<div class="input-group-btn"> |
|||
<button class="btn btn-default" type="submit"> |
|||
<span class="glyphicon glyphicon-search"></span> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<label class="sr-only" for="dialtime">{% trans 'Find by dates' %}</label> |
|||
<div class="input-group input-group-sm"> |
|||
<input type="datetime-local" class="form-control" id="dialtime" placeholder="{% trans 'calldate' %}" name="sd"{% if sd %} value="{{ sd }}"{% endif %}> |
|||
<div class="input-group-btn"> |
|||
<button class="btn btn-default" type="submit"> |
|||
<span class="glyphicon glyphicon-search"></span> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% for log in logs %} |
|||
<tr> |
|||
{% with lurl=log.url %} |
|||
<td class="btn-group btn-group-xs btn-group-justify"> |
|||
<button class="btn btn-default player-btn disabled"> |
|||
<span class="glyphicon glyphicon-play"></span> |
|||
<audio preload="metadata" src="{{ lurl|default:'#' }}"></audio> |
|||
</button> |
|||
<a href="{{ lurl|default:'#' }}" class="btn btn-default disabled" title="{% trans 'Download' %}"> |
|||
<span class="glyphicon glyphicon-download-alt"></span> |
|||
</a> |
|||
</td> |
|||
<td>{{ log.calldate|date:'d M, H:i:s' }}</td> |
|||
<td>{{ log.src|abon_if_telephone|safe }}</td> |
|||
<td>{{ log.dst|abon_if_telephone|safe }}</td> |
|||
<td>{{ log.duration }}</td> |
|||
<td>{{ log.start|date:'d M, H:i:s' }}</td> |
|||
<td>{{ log.answer|date:'d M, H:i:s' }}</td> |
|||
<td>{{ log.end|date:'d M, H:i:s' }}</td> |
|||
<td>{{ log.locate_disposition }}</td> |
|||
{% endwith %} |
|||
</tr> |
|||
{% empty %} |
|||
<tr> |
|||
<td colspan="9">{% trans 'Calls was not found' %}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
{% endblock %} |
|||
@ -1,23 +0,0 @@ |
|||
{% load i18n bootstrap3 %} |
|||
<form action="{% url 'dialapp:send_sms' %}?path={{ path|default:'' }}" method="post"> {% csrf_token %} |
|||
<div class="modal-header primary"> |
|||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
|||
<h4 class="modal-title"><span class="glyphicon glyphicon-envelope"></span>{% trans 'Send sms' %}</h4> |
|||
</div> |
|||
|
|||
{% include '_messages.html' %} |
|||
|
|||
<div class="modal-body"> |
|||
|
|||
{% bootstrap_icon 'earphone' as ic %} |
|||
{% bootstrap_field form.dst label_class='col-sm-2' field_class='col-sm-10' addon_before=ic %} |
|||
|
|||
{% bootstrap_icon 'pencil' as ic %} |
|||
{% bootstrap_field form.text label_class='col-sm-2' field_class='col-sm-10' addon_before=ic %} |
|||
|
|||
<button type="submit" class="btn btn-sm btn-success"> |
|||
<span class="glyphicon glyphicon-send"></span> {% trans 'Send' %} |
|||
</button> |
|||
</div> |
|||
|
|||
</form> |
|||
@ -1,48 +0,0 @@ |
|||
{% extends request.is_ajax|yesno:'nullcont.htm,ext.html' %} |
|||
{% load i18n telephone_filters %} |
|||
{% block content %} |
|||
|
|||
<div class="table-responsive"> |
|||
<table class="table table-striped table-bordered"> |
|||
<thead> |
|||
<tr> |
|||
<th>{% trans 'Play' %}</th> |
|||
<th>{% trans 'calldate' %}</th> |
|||
<th>{% trans 'src' %}</th> |
|||
<th>{% trans 'duration' %}</th> |
|||
<th>{% trans 'start' %}</th> |
|||
<th>{% trans 'answer' %}</th> |
|||
<th>{% trans 'end' %}</th> |
|||
<th>{% trans 'disposition' %}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% for vmail in vmessages %} |
|||
<tr> |
|||
<td class="btn-group btn-group-xs"> |
|||
<button class="btn btn-default player-btn disabled"> |
|||
<span class="glyphicon glyphicon-play"></span> |
|||
<audio preload="metadata" src="{{ vmail.path_to_media }}/{{ vmail.calldate|date:"YmdHi" }}-{{ vmail.src }}-{{ vmail.dst }}.wav"></audio> |
|||
</button> |
|||
<a href="{{ vmail.path_to_media }}/{{ vmail.calldate|date:"YmdHi" }}-{{ vmail.src }}-{{ vmail.dst }}.wav" class="btn btn-default disabled" target="_blank"> |
|||
<span class="glyphicon glyphicon-download-alt"></span> |
|||
</a> |
|||
</td> |
|||
<td>{{ vmail.calldate|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ vmail.src|abon_if_telephone|safe }}</td> |
|||
<td>{{ vmail.duration }}</td> |
|||
<td>{{ vmail.start|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ vmail.answer|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ vmail.end|date:'d E Y, H:i:s' }}</td> |
|||
<td>{{ vmail.locate_disposition }}</td> |
|||
</tr> |
|||
{% empty %} |
|||
<tr> |
|||
<td colspan="8">{% trans 'Calls was not found' %}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
|
|||
{% endblock %} |
|||
@ -1,20 +0,0 @@ |
|||
import re |
|||
from django import template |
|||
from django.shortcuts import resolve_url |
|||
from django.template.defaultfilters import stringfilter |
|||
|
|||
register = template.Library() |
|||
|
|||
|
|||
@register.filter |
|||
@stringfilter |
|||
def abon_if_telephone(value): |
|||
"""Возвращаем ссыль на абонента если передали номер телефона""" |
|||
if re.match(r'^\+?\d+$', value): |
|||
if value[0] != '+': |
|||
value = '+' + value |
|||
url = resolve_url('dialapp:to_abon', tel=value) |
|||
a = '<a href="%s">%s</a>' % (url, value) |
|||
return a |
|||
else: |
|||
return value |
|||
@ -1,3 +0,0 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
|||
@ -1,15 +0,0 @@ |
|||
from django.urls import path, re_path |
|||
from . import views |
|||
|
|||
app_name = 'dialing_app' |
|||
|
|||
urlpatterns = [ |
|||
path('', views.LastCallsListView.as_view(), name='home'), |
|||
path('filter/', views.DialsFilterListView.as_view(), name='vfilter'), |
|||
re_path('^to_abon(?P<tel>\+?\d+)/$', views.to_abon, name='to_abon'), |
|||
path('requests/', views.VoiceMailRequestsListView.as_view(), name='vmail_request'), |
|||
path('reports/', views.VoiceMailReportsListView.as_view(), name='vmail_report'), |
|||
path('sms/in/', views.InboxSMSListView.as_view(), name='inbox_sms'), |
|||
path('sms/send/', views.send_sms, name='send_sms'), |
|||
path('api/sms/', views.SmsManager.as_view()) |
|||
] |
|||
@ -1,221 +0,0 @@ |
|||
from datetime import datetime |
|||
from subprocess import run |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.contrib import messages |
|||
|
|||
from django.db import ProgrammingError |
|||
from django.shortcuts import redirect, render |
|||
from django.utils.translation import gettext_lazy as _ |
|||
from django.utils.decorators import method_decorator |
|||
from django.views.generic import ListView |
|||
from guardian.decorators import permission_required_or_403 as permission_required |
|||
from django.db.models import Q |
|||
from django.conf import settings |
|||
|
|||
from abonapp.models import Abon |
|||
from djing.global_base_views import SecureApiView |
|||
from djing import JSONType |
|||
from djing.lib import safe_int |
|||
from djing.lib.decorators import only_admins, json_view |
|||
from .models import AsteriskCDR, SMSModel, SMSOut |
|||
from .forms import SMSOutForm |
|||
|
|||
|
|||
login_decs = login_required, only_admins |
|||
|
|||
|
|||
class BaseListView(ListView): |
|||
http_method_names = 'get', |
|||
paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) |
|||
|
|||
|
|||
@method_decorator(login_decs, name='dispatch') |
|||
@method_decorator(permission_required('dialing_app.change_asteriskcdr'), name='dispatch') |
|||
class LastCallsListView(BaseListView): |
|||
template_name = 'index.html' |
|||
context_object_name = 'logs' |
|||
queryset = AsteriskCDR.objects.exclude(userfield='request') |
|||
|
|||
def get(self, request, *args, **kwargs): |
|||
try: |
|||
return super(LastCallsListView, self).get(request, *args, **kwargs) |
|||
except ProgrammingError as e: |
|||
messages.error(self.request, e) |
|||
return redirect('abonapp:group_list') |
|||
|
|||
def get_context_data(self, **kwargs): |
|||
context = super(LastCallsListView, self).get_context_data(**kwargs) |
|||
context['title'] = _('Last calls') |
|||
return context |
|||
|
|||
|
|||
@login_required |
|||
@only_admins |
|||
def to_abon(request, tel): |
|||
abon = Abon.objects.filter( |
|||
Q(telephone__icontains=tel) | |
|||
Q(additional_telephones__telephone__icontains=tel) |
|||
) |
|||
abon_count = abon.count() |
|||
if abon_count > 1: |
|||
messages.warning( |
|||
request, _('Multiple users with the telephone number') |
|||
) |
|||
elif abon_count == 0: |
|||
messages.error(request, _('User with the telephone number not found')) |
|||
return redirect('dialapp:home') |
|||
abon = abon[0] |
|||
if abon.group: |
|||
return redirect( |
|||
'abonapp:abon_home', gid=abon.group.pk, |
|||
uname=abon.username |
|||
) |
|||
else: |
|||
return redirect('abonapp:group_list') |
|||
|
|||
|
|||
@method_decorator(login_decs, name='dispatch') |
|||
class VoiceMailRequestsListView(BaseListView): |
|||
template_name = 'vmail.html' |
|||
context_object_name = 'vmessages' |
|||
queryset = AsteriskCDR.objects.filter(userfield='request') |
|||
|
|||
def get_context_data(self, **kwargs): |
|||
context = super(VoiceMailRequestsListView, self).get_context_data(**kwargs) |
|||
context['title'] = _('Voice mail request') |
|||
return context |
|||
|
|||
|
|||
class VoiceMailReportsListView(VoiceMailRequestsListView): |
|||
queryset = AsteriskCDR.objects.filter(userfield='report') |
|||
|
|||
def get_context_data(self, **kwargs): |
|||
context = super(VoiceMailRequestsListView, self).get_context_data(**kwargs) |
|||
context['title'] = _('Voice mail report') |
|||
return context |
|||
|
|||
|
|||
@method_decorator(login_decs, name='dispatch') |
|||
class DialsFilterListView(BaseListView): |
|||
context_object_name = 'logs' |
|||
template_name = 'index.html' |
|||
|
|||
def get_context_data(self, **kwargs): |
|||
context = super(DialsFilterListView, self).get_context_data(**kwargs) |
|||
context['title'] = _('Find dials') |
|||
context['s'] = self.request.GET.get('s') |
|||
context['sd'] = self.request.GET.get('sd') |
|||
return context |
|||
|
|||
def get_queryset(self): |
|||
s = self.request.GET.get('s') |
|||
sd = self.request.GET.get('sd') |
|||
if isinstance(s, str) and s != '': |
|||
cdr_q = Q(src__icontains=s) | Q(dst__icontains=s) |
|||
else: |
|||
cdr_q = None |
|||
try: |
|||
if isinstance(sd, str) and sd != '': |
|||
sd_date = datetime.strptime(sd, '%Y-%m-%d') |
|||
if cdr_q: |
|||
cdr_q |= Q(calldate__date=sd_date) |
|||
else: |
|||
cdr_q = Q(calldate__date=sd_date) |
|||
except ValueError: |
|||
messages.add_message(self.request, messages.ERROR, _('Make sure that your date format is correct')) |
|||
if cdr_q is None: |
|||
cdr = AsteriskCDR.objects.all() |
|||
else: |
|||
cdr = AsteriskCDR.objects.filter(cdr_q) |
|||
return cdr |
|||
|
|||
|
|||
@method_decorator(login_decs, name='dispatch') |
|||
@method_decorator(permission_required('dialing_app.view_smsmodel'), name='dispatch') |
|||
class InboxSMSListView(BaseListView): |
|||
template_name = 'inbox_sms.html' |
|||
context_object_name = 'sms_messages' |
|||
model = SMSModel |
|||
|
|||
|
|||
@login_required |
|||
@only_admins |
|||
@permission_required('dialing_app.add_smsout') |
|||
def send_sms(request): |
|||
path = request.GET.get('path') |
|||
initial_dst = request.GET.get('dst') |
|||
if request.method == 'POST': |
|||
frm = SMSOutForm(request.POST) |
|||
if frm.is_valid(): |
|||
frm.save() |
|||
messages.success(request, _('Message was enqueued for sending')) |
|||
pidfile_name = '/run/dialing.py.pid' |
|||
try: |
|||
with open(pidfile_name, 'r') as f: |
|||
pid = int(f.read()) |
|||
run(['/usr/bin/kill', '-SIGUSR1', str(pid)]) |
|||
except FileNotFoundError: |
|||
print('Failed sending, %s not found' % pidfile_name) |
|||
if path: |
|||
return redirect(path) |
|||
else: |
|||
return redirect('dialapp:inbox_sms') |
|||
else: |
|||
messages.error(request, _('fix form errors')) |
|||
else: |
|||
frm = SMSOutForm(initial={'dst': initial_dst}) |
|||
return render(request, 'modal_send_sms.html', { |
|||
'form': frm, |
|||
'path': path |
|||
}) |
|||
|
|||
|
|||
class SmsManager(SecureApiView): |
|||
# |
|||
# Api view for management sms from dongle |
|||
# |
|||
http_method_names = ('get',) |
|||
|
|||
@staticmethod |
|||
def bad_cmd() -> JSONType: |
|||
return {'text': 'Command is not allowed'} |
|||
|
|||
@method_decorator(json_view) |
|||
def get(self, request, *args, **kwargs): |
|||
cmd = request.GET.get('cmd') |
|||
data = request.GET.dict() |
|||
handler = getattr(self, cmd.lower(), self.bad_cmd) |
|||
del data['cmd'] |
|||
del data['sign'] |
|||
return handler(**data) |
|||
|
|||
@staticmethod |
|||
def save_sms(**kwargs) -> JSONType: |
|||
sms = SMSModel.objects.create( |
|||
who=kwargs.get('who'), |
|||
dev=kwargs.get('dev'), |
|||
text=kwargs.get('text') |
|||
) |
|||
return {'status': 'ok', 'sms_id': sms.pk} |
|||
|
|||
@staticmethod |
|||
def update_status(**kwargs) -> JSONType: |
|||
msg_id = safe_int(kwargs.get('mid')) |
|||
if msg_id != 0: |
|||
status = kwargs.get('status') |
|||
update_count = SMSOut.objects.filter(pk=msg_id).update(status=status) |
|||
return { |
|||
'text': 'Status updated', |
|||
'update_count': update_count |
|||
} |
|||
return {'text': 'Bad mid parameter'} |
|||
|
|||
@staticmethod |
|||
def get_new() -> JSONType: |
|||
msgs = SMSOut.objects.filter(status='nw').defer('status') |
|||
res = [{ |
|||
'when': round(m.timestamp), |
|||
'dst': m.dst, |
|||
'text': m.text |
|||
} for m in msgs] |
|||
return res |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue