welco/welco/sources/phone/views.py

232 lines
8.5 KiB
Python

# welco - multichannel request processing
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import logging
from django import template
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponse
from django.utils import six
from django.utils.encoding import force_text
from django.utils.timezone import now
from django.views.generic import TemplateView
from .models import PhoneCall, PhoneLine
class Home(object):
source_key = 'phone'
def __init__(self, request, **kwargs):
self.request = request
self.kwargs = kwargs
def render(self):
zone = PhoneZone()
zone.request = self.request
context = zone.get_context_data()
# always use ajax to get list of phone calls
del context['phonecalls']
tmpl = template.loader.get_template('welco/phone_home.html')
return tmpl.render(context, request=self.request)
class PhoneZone(TemplateView):
template_name = 'welco/phone_home.html'
def get_context_data(self, **kwargs):
if settings.PHONE_AUTOTAKE_MELLON_USERNAME:
username = self.request.session.get('mellon_session', {}).get('username')
if username:
# user is from SSO, username is a phone line (callee), create a link to it
username = username[0].split('@', 1)[0][:80] # remove realm
if username:
PhoneLine.take(callee=username, user=self.request.user)
context = super(PhoneZone, self).get_context_data(**kwargs)
context['source_type'] = ContentType.objects.get_for_model(PhoneCall)
context['phonelines'] = PhoneLine.objects.filter(users__id=self.request.user.id)
context['phonecalls'] = PhoneCall.get_current_calls(self.request.user)
return context
zone = csrf_exempt(PhoneZone.as_view())
@csrf_exempt
def call_event(request):
'''Log a new call start or stop, input is JSON:
{
'event': 'start' or 'stop',
'caller': '003399999999',
'callee': '102',
'data': {
'user': 'zozo',
},
}
'''
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert set(payload.keys()) <= set(['event', 'caller', 'callee', 'data']), \
'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert set(['event', 'caller', 'callee']) <= set(payload.keys()), \
'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert payload['event'] in ('start', 'stop'), 'event must be "start" or "stop"'
assert isinstance(payload['caller'], six.string_types), 'caller must be a string'
assert isinstance(payload['callee'], six.string_types), 'callee must be a string'
if 'data' in payload:
assert isinstance(payload['data'], dict), 'data must be a JSON object'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(json.dumps({'err': 1, 'msg':
force_text(e)}),
content_type='application/json')
# janitoring: stop active calls to the callee
if settings.PHONE_ONE_CALL_PER_CALLEE:
logger.info('stop all calls to %s', payload['callee'])
PhoneCall.objects.filter(callee=payload['callee'],
stop__isnull=True).update(stop=now())
else:
logger.info('stop call from %s to %s', payload['caller'], payload['callee'])
PhoneCall.objects.filter(caller=payload['caller'],
callee=payload['callee'],
stop__isnull=True).update(stop=now())
if payload['event'] == 'start':
# start a new call
kwargs = {
'caller': payload['caller'],
'callee': payload['callee'],
}
if 'data' in payload:
kwargs['data'] = json.dumps(payload['data'])
PhoneCall.objects.create(**kwargs)
logger.info('start call from %s to %s', payload['caller'], payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')
@login_required
def active_call(request, *args, **kwargs):
call = PhoneCall.objects.get(id=kwargs.get('pk'))
result = {
'caller': call.caller,
'callee': call.callee,
'active': not(bool(call.stop)),
'start_timestamp': call.start.strftime('%Y-%m-%dT%H:%M:%S'),
}
return HttpResponse(json.dumps(result, indent=2),
content_type='application/json')
@login_required
def current_calls(request):
'''Returns the list of current calls for current user as JSON:
{
'err': 0,
'data': {
'calls': [
{
'caller': '00334545445',
'callee': '102',
'data': { ... },
},
...
],
'lines': [
'102',
],
'all-lines': [
'102',
],
}
}
lines are number the user is currently watching, all-lines is all
registered numbers.
'''
all_callees = PhoneCall.get_all_callees()
callees = PhoneLine.get_callees(request.user)
phonecalls = PhoneCall.get_current_calls(request.user)
calls = []
payload = {
'err': 0,
'data': {
'calls': calls,
'lines': list(callees),
'all-lines': list(all_callees),
},
}
for call in phonecalls:
calls.append({
'caller': call.caller,
'callee': call.callee,
'start': call.start.isoformat('T').split('.')[0],
})
if call.data:
calls[-1]['data'] = json.loads(call.data)
response = HttpResponse(content_type='application/json')
json.dump(payload, response, indent=2)
return response
@csrf_exempt
@login_required
def take_line(request):
'''Take a line, input is JSON:
{ 'callee': '003369999999' }
'''
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert list(payload.keys()) == ['callee'], 'payload must have only one key: callee'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(json.dumps({'err': 1, 'msg':
force_text(e)}),
content_type='application/json')
PhoneLine.take(payload['callee'], request.user)
logger.info(u'user %s took line %s', request.user, payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')
@csrf_exempt
@login_required
def release_line(request):
'''Release a line, input is JSON:
{ 'callee': '003369999999' }
'''
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert list(payload.keys()) == ['callee'], 'payload must have only one key: callee'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(json.dumps({'err': 1, 'msg':
force_text(e)}),
content_type='application/json')
PhoneLine.release(payload['callee'], request.user)
logger.info(u'user %s released line %s', request.user, payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')