Browse Source

Make up web notifications

devel
bashmak 8 years ago
parent
commit
bd18deaf32
  1. 1
      chatbot/admin.py
  2. 78
      chatbot/locale/ru/LC_MESSAGES/django.po
  3. 59
      chatbot/migrations/0002_auto_20171214_1517.py
  4. 52
      chatbot/models.py
  5. 14
      chatbot/telebot.py
  6. 1
      dialing_app/models.py
  7. 69
      docs/dev.md
  8. 2
      msg_app/models.py
  9. 3
      msg_app/urls.py
  10. 17
      msg_app/views.py
  11. BIN
      static/img/noticon.png
  12. 49
      static/js/my.js
  13. 4
      taskapp/handle.py
  14. 3
      taskapp/urls.py
  15. 18
      taskapp/views.py

1
chatbot/admin.py

@ -3,3 +3,4 @@ from . import models
admin.site.register(models.MessageHistory)
admin.site.register(models.TelegramBot)
admin.site.register(models.MessageQueue)

78
chatbot/locale/ru/LC_MESSAGES/django.po

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-24 23:49+0300\n"
"POT-Creation-Date: 2017-12-14 15:04+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov <nerosketch@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -20,15 +20,59 @@ msgstr ""
"%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"
#: chatbot/telebot.py:61
#: models.py:13
msgid "Employee"
msgstr "Сотрудник"
#: models.py:14
msgid "Telegram chat id"
msgstr "Номер чата из telegram"
#: models.py:21
msgid "Telegram bot"
msgstr "Telegram бот"
#: models.py:22
msgid "Telegram bots"
msgstr "Telegram боты"
#: models.py:35
msgid "Message history"
msgstr "История собщений"
#: models.py:36
msgid "Message histories"
msgstr "Истории собщений"
#: models.py:53
msgid "Target employee"
msgstr "Сотрудник"
#: models.py:54
msgid "Message"
msgstr "Сообщение"
#: models.py:59
msgid "Status of message"
msgstr "Статус сообщения"
#: models.py:61
msgid "App tag"
msgstr "Тэг приложения"
#: models.py:70 models.py:71
msgid "Message queue"
msgstr "Очередь оповещений"
#: telebot.py:63
msgid "Let's get acquainted, what is your name? Write your login from billing."
msgstr "Давай знакомиться, как тебя зовут? Напиши свой логин из биллинга."
#: chatbot/telebot.py:81
#: telebot.py:84
msgid "I do not know the answer to this yet."
msgstr "Я пока не знаю ответа на это"
#: chatbot/telebot.py:100
#: telebot.py:104
msgid ""
"You are not found in the database, check that it correctly pointed out its "
"LOGIN. Try again"
@ -36,25 +80,29 @@ msgstr ""
"Ты не найден в базе, проверь что правильно указал именно свой ЛОГИН. "
"Попробуй ещё"
#: chatbot/telebot.py:123
#: telebot.py:120
msgid "Let's ping, write ip. It will be necessary to wait 10 seconds"
msgstr "Давай пинганём, напиши ip. Нужно будет подождать 10 сек"
#: telebot.py:127
msgid "It's not like ip address, try again"
msgstr "Это не похоже на ip адрес, попробуй ещё"
#: chatbot/telebot.py:126
#: telebot.py:130
#, python-format
msgid "You're '%s', right?"
msgstr "Ты ведь %s ?"
#: chatbot/telebot.py:136
#: telebot.py:138
msgid "Telegram bot token not found"
msgstr "Токен для бота Telegram не найден"
#: telebot.py:143
#, python-format
msgid "Recipient '%s' does not subscribed on notifications"
msgstr "%s не подписан на оповещения"
msgid "Let's ping, write ip. It will be necessary to wait 10 seconds"
msgstr "Давай пинганём, напиши ip. Нужно будет подождать 10 сек"
msgid "Yes, it's nice to meet% s, I will notify you about events in billing. Successful work;)"
msgstr "Да, приятно познакомиться %s, я буду оповещать тебя о событиях в биллинге. Удачной работы ;)"
msgid "Telegram bot token not found"
msgstr "Токен для бота Telegram не найден"
msgid ""
"Yes, it's nice to meet %(username)s, I will notify you about events in billing. Successful work ;)"
msgstr ""
"Да, приятно познакомиться %(username)s, я буду оповещать тебя о событиях в биллинге. Удачной работы ;)"

59
chatbot/migrations/0002_auto_20171214_1517.py

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-12-14 15:17
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):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('chatbot', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='MessageQueue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.CharField(max_length=255, verbose_name='Message')),
('status', models.CharField(choices=[('n', 'New'), ('r', 'Read')], default='n', max_length=1, verbose_name='Status of message')),
('tag', models.CharField(default='none', max_length=6, verbose_name='App tag')),
('target_employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Target employee')),
],
options={
'verbose_name': 'Message queue',
'verbose_name_plural': 'Message queue',
'db_table': 'chat_message_queue',
},
),
migrations.AlterModelOptions(
name='messagehistory',
options={'verbose_name': 'Message history', 'verbose_name_plural': 'Message histories'},
),
migrations.AlterModelOptions(
name='telegrambot',
options={'verbose_name': 'Telegram bot', 'verbose_name_plural': 'Telegram bots'},
),
migrations.AlterField(
model_name='telegrambot',
name='chat_id',
field=models.PositiveIntegerField(default=0, verbose_name='Telegram chat id'),
),
migrations.AlterField(
model_name='telegrambot',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Employee'),
),
migrations.AlterModelTable(
name='messagehistory',
table='chat_message_history',
),
migrations.AlterModelTable(
name='telegrambot',
table='chat_telegram_bot',
),
]

52
chatbot/models.py

@ -1,3 +1,4 @@
from django.utils.translation import ugettext as _
from django.db import models
from django.conf import settings
@ -9,11 +10,16 @@ class ChatException(Exception):
class TelegramBot(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL)
chat_id = models.PositiveIntegerField(default=0)
user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_('Employee'))
chat_id = models.PositiveIntegerField(_('Telegram chat id'), default=0)
def __str__(self):
return self.user.get_full_name() + ' - ' + str(self.chat_id)
return "%s - %d" % (self.user.get_full_name(), self.chat_id)
class Meta:
db_table = 'chat_telegram_bot'
verbose_name = _('Telegram bot')
verbose_name_plural = _('Telegram bots')
class MessageHistory(models.Model):
@ -23,3 +29,43 @@ class MessageHistory(models.Model):
def __str__(self):
return self.message
class Meta:
db_table = 'chat_message_history'
verbose_name = _('Message history')
verbose_name_plural = _('Message histories')
class MessageQueueManager(models.Manager):
def pop(self, user, tag='none'):
msgs = self.filter(target_employee=user, status='n', tag=tag)[:1].only('message').values('id', 'message')
if len(msgs) > 0:
self.filter(id=msgs[0]['id']).delete()
return msgs[0]['message']
def push(self, msg, user, tag='none'):
msg = self.create(target_employee=user, message=msg, tag=tag)
return msg
class MessageQueue(models.Model):
target_employee = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_('Target employee'))
message = models.CharField(_('Message'), max_length=255)
STATUSES = (
('n', 'New'),
('r', 'Read')
)
status = models.CharField(_('Status of message'), max_length=1, choices=STATUSES, default='n')
# tag каждое приложение ставит своим чтоб делить сообщения между этими приложениями
tag = models.CharField(_('App tag'), max_length=6, default='none')
objects = MessageQueueManager()
def __str__(self):
return self.message
class Meta:
db_table = 'chat_message_queue'
verbose_name = _('Message queue')
verbose_name_plural = _('Message queue')

14
chatbot/telebot.py

@ -5,7 +5,7 @@ import socket
import collections
from django.utils.translation import ugettext as _
from urllib3.exceptions import ProtocolError
from .models import TelegramBot, ChatException
from .models import TelegramBot, ChatException, MessageQueue
from chatbot.models import MessageHistory
from accounts_app.models import UserProfile
from django.conf import settings
@ -19,7 +19,7 @@ class DjingTelebot(helper.ChatHandler):
_chat_id = 0
def __init__(self, seed_tuple, **kwargs):
super().__init__(seed_tuple, **kwargs)
super(DjingTelebot, self).__init__(seed_tuple, **kwargs)
self.cmds = {
'ping': self.ping,
'iam': self.say_me
@ -104,8 +104,8 @@ class DjingTelebot(helper.ChatHandler):
self._question(_("You are not found in the database, check that it correctly pointed out its LOGIN. Try again"),
self.question_name)
return
self._sent_reply("Yes, it's nice to meet %s, I will notify you about events in billing. Successful work;)"
% profile.get_full_name())
self._sent_reply(_("Yes, it's nice to meet %(username)s, I will notify you about events in billing. Successful work ;)")
% {'username': profile.get_full_name()})
# заканчивается время диалога
# ex - время ожидания (timeout=ex в pave_event_space)
@ -130,9 +130,10 @@ class DjingTelebot(helper.ChatHandler):
self._sent_reply(_("You're '%s', right?") % self._current_user.get_full_name())
# Просто отправляем текст оповещения указанному админу
def send_notify(msg_text, account):
# Просто отправляем текст оповещения указанной учётке
def send_notify(msg_text, account, tag='none'):
try:
MessageQueue.objects.push(msg=msg_text, user=account, tag=tag)
if token is None:
raise ChatException(_('Telegram bot token not found'))
tb = TelegramBot.objects.get(user=account)
@ -142,4 +143,3 @@ def send_notify(msg_text, account):
raise ChatException(_("Recipient '%s' does not subscribed on notifications") % account.get_full_name())
except ProtocolError as e:
raise ChatException(e)

1
dialing_app/models.py

@ -64,3 +64,4 @@ class AsteriskCDR(models.Model):
class Meta:
db_table = 'cdr'
managed = False

69
docs/dev.md

@ -289,3 +289,72 @@ def add_user_range(self, user_list):
def add_tariff_range(self, tariff_list):
pass
```
## Отправляем оповещения
Для того чтоб оправить важное сообщение работнику через все возможные настроенные системы(смс, телеграм, браузер) мы можем
воспользоваться одной процедурой из модуля **chatbot**.
```python
from chatbot.telebot import send_notify
send_notify(msg_text='Text message',account=employee_profile, tag='apptag')
```
Процедура **send_notify** принимает 3 параметра. 2 первых обязательны а последний не обязаелен.
*msg_text* - Текст сообщения
*account* - Учётка работника которому отправляем сообщение
*tag* - Тэг сообщения, это поле предназначено для фильтрации ваших сообщений в вашем приложении. Каждое приложение в пределах
своих вызовов использует один и тот жеж уникальный тэг. Для примера приложение личных сообщений видит сообщения только для себя
с помощью тега *msgapp*, и вы не спутаете ваши сообщения с сообщениями из модуля, например, задач который использует тэг *taskap*.
#### Получение оповещения
```python
from chatbot.models import MessageQueue
msg = MessageQueue.objects.pop(user=employee_profile, tag='apptag')
```
Метод **pop** плучает первое сообщение и удаляет его.
#### Отображаем оповещение в браузере
Чтоб отобразить **WebNotification** добавте в файле скриптов *static/js/my.js* строку вида:
```javascript
$(document).notifys({news_url: '/url/to/your_view', check_interval: 60});
```
Тут *news_url* это путь к вашему представления которое возвращает новые оповещения, а *check_interval* - это интервал обращения к вашему представлению.
Представление для Web оповещений выглядит примерно так:
```python
@login_required
@only_admins
def check_news(request):
msg = MessageQueue.objects.pop(user=request.user, tag='apptag')
if msg is not None:
r = {
'exist': True,
'content': msg,
'title': 'Message title'
}
else:
r = {'exist': False}
return HttpResponse(dumps(r))
```
Убедитесь что вашему представлению не будет доступа от абонентов, об этом позаботится декоратор *only_admins* из *mydefs*. *mydefs* лежит в корне проекта.
После получения сообщения надо вернуть словарь с параметрами:
*exist* - Логическое значение, обозначает есть или нет информации в ответе. Если *exist* == True тогда возвращае ещё *content* и *title*.
*content* - Соответственно содержимое оповещения.
*title* - Заголовок оповещения.

2
msg_app/models.py

@ -1,6 +1,7 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from accounts_app.models import UserProfile
from chatbot.telebot import send_notify
class MessageError(Exception):
@ -166,6 +167,7 @@ class Conversation(models.Model):
if participant == author:
continue
MessageStatus.objects.create(msg=msg, user=participant)
send_notify(msg_text=text,account=author, tag='msgapp')
return msg
def remove_message(self, msg):

3
msg_app/urls.py

@ -6,5 +6,6 @@ urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^new$', views.new_conversation, name='new_conversation'),
url(r'^(?P<conv_id>\d+)/$', views.to_conversation, name='to_conversation'),
url(r'^(?P<conv_id>\d+)/(?P<msg_id>\d+)/del$', views.remove_msg, name='remove_msg')
url(r'^(?P<conv_id>\d+)/(?P<msg_id>\d+)/del$', views.remove_msg, name='remove_msg'),
url(r'^check_news$', views.check_news, name='check_news')
]

17
msg_app/views.py

@ -1,9 +1,12 @@
from json import dumps
from django.contrib.auth.decorators import login_required
from django.contrib.gis.shortcuts import render_to_text
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from django.shortcuts import render, redirect, get_object_or_404
from chatbot.models import MessageQueue
from mydefs import pag_mn
from .models import Conversation, MessageError, Message
@ -70,3 +73,17 @@ def remove_msg(request, conv_id, msg_id):
conversation_id = msg.conversation.pk
msg.delete()
return redirect('msg_app:to_conversation', conversation_id)
@login_required
def check_news(request):
msg = MessageQueue.objects.pop(user=request.user, tag='msgapp')
if msg is not None:
r = {
'exist': True,
'content': msg,
'title': _('Task')
}
else:
r = {'exist': False}
return HttpResponse(dumps(r))

BIN
static/img/noticon.png

After

Width: 192  |  Height: 192  |  Size: 12 KiB

49
static/js/my.js

@ -173,6 +173,52 @@ $(document).ajaxError(function (ev, jqXHR, ajaxSettings, thrownError) {
})(jQuery);
(function($){
$.fn.notifys = function(opt){
var settings = $.extend({
news_url: null,
check_interval: 60
}, opt);
var notifShow = function(title, content){
var perm = Notification.permission.toLowerCase();
if(perm == "granted"){
curnotify = new Notification(title, {
tag: 'djing-notify',
body: content,
icon: '/static/img/noticon.png'}
);
}else if(perm == "default"){
Notification.requestPermission(on_ask_perm);
}
}
var on_ask_perm = function(r){
notifShow("Thanks for letting notify you");
}
var check_news = function(){
var perm = Notification.permission.toLowerCase();
if(perm == "granted" && settings.news_url){
$.getJSON(settings.news_url, function(r){
if(r.exist){
notifShow(r.title, r.content);
}/*else console.log('No news from '+settings.news_url);*/
});
}
}
if(settings.news_url){
// прверяем новости раз в минуту
var tiid = setInterval(check_news, settings.check_interval*1000);
Notification.requestPermission(on_ask_perm);
}
}
})(jQuery);
$(document).ready(function () {
// Live html5 image preview
@ -247,4 +293,7 @@ $(document).ready(function () {
$('.btn_ajloader').ajloader({'dst_block': '#id_block_devices'});
$(document).notifys({news_url: '/tasks/check_news', check_interval: 10});
$(document).notifys({news_url: '/msg/check_news', check_interval: 55});
});

4
taskapp/handle.py

@ -40,11 +40,11 @@ def handle(task, author, recipients, abon_group):
if task.state == 'F' or task.state == 'C':
# Если задача завершена или провалена то отправляем одно оповещение автору
try:
send_notify(fulltext, author)
send_notify(fulltext, author, tag='taskap')
except ChatException as e:
raise TaskException(e)
else:
send_notify(fulltext, dst_account)
send_notify(fulltext, dst_account, tag='taskap')
except ChatException as e:
errors.append(e)
if len(errors) > 0:

3
taskapp/urls.py

@ -16,5 +16,6 @@ urlpatterns = [
url(r'^finished$', views.finished_tasks, name='finished_tasks'),
url(r'^own$', views.own_tasks, name='own_tasks'),
url(r'^my$', views.my_tasks, name='my_tasks'),
url(r'^all$', views.all_tasks, name='all_tasks')
url(r'^all$', views.all_tasks, name='all_tasks'),
url(r'^check_news$', views.check_news, name='check_news')
]

18
taskapp/views.py

@ -1,12 +1,15 @@
# coding=utf-8
from json import dumps
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from abonapp.models import Abon
from django.utils.translation import ugettext as _
from datetime import date
from guardian.decorators import permission_required_or_403 as permission_required
from chatbot.models import MessageQueue
from .handle import TaskException
from .models import Task
@ -202,3 +205,18 @@ def remind(request, task_id):
except TaskException as e:
messages.error(request, e)
return redirect('taskapp:home')
@login_required
@only_admins
def check_news(request):
msg = MessageQueue.objects.pop(user=request.user, tag='taskap')
if msg is not None:
r = {
'exist': True,
'content': msg,
'title': _('Task')
}
else:
r = {'exist': False}
return HttpResponse(dumps(r))
Loading…
Cancel
Save