# 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 . 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')