Projet

Général

Profil

0001-add-phone-calls-connector-29829.patch

Thomas Noël, 16 janvier 2019 17:49

Télécharger (10,4 ko)

Voir les différences:

Subject: [PATCH] add phone calls connector (#29829)

 passerelle/apps/phonecalls/__init__.py        |   0
 .../phonecalls/migrations/0001_initial.py     |  55 +++++++
 .../apps/phonecalls/migrations/__init__.py    |   0
 passerelle/apps/phonecalls/models.py          | 148 ++++++++++++++++++
 passerelle/settings.py                        |   1 +
 5 files changed, 204 insertions(+)
 create mode 100644 passerelle/apps/phonecalls/__init__.py
 create mode 100644 passerelle/apps/phonecalls/migrations/0001_initial.py
 create mode 100644 passerelle/apps/phonecalls/migrations/__init__.py
 create mode 100644 passerelle/apps/phonecalls/models.py
passerelle/apps/phonecalls/migrations/0001_initial.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.17 on 2019-01-16 14:36
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6
import django.db.models.deletion
7
import jsonfield.fields
8

  
9

  
10
class Migration(migrations.Migration):
11

  
12
    initial = True
13

  
14
    dependencies = [
15
        ('base', '0010_loggingparameters_trace_emails'),
16
    ]
17

  
18
    operations = [
19
        migrations.CreateModel(
20
            name='Call',
21
            fields=[
22
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23
                ('callee', models.CharField(max_length=64)),
24
                ('caller', models.CharField(max_length=64)),
25
                ('start_timestamp', models.DateTimeField(auto_now_add=True)),
26
                ('end_timestamp', models.DateTimeField(default=None, null=True)),
27
                ('details', jsonfield.fields.JSONField(default={})),
28
            ],
29
            options={
30
                'ordering': ['-end_timestamp', '-start_timestamp'],
31
                'get_latest_by': 'start_timestamp',
32
                'verbose_name': 'Phone Call',
33
            },
34
        ),
35
        migrations.CreateModel(
36
            name='PhoneCalls',
37
            fields=[
38
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
39
                ('title', models.CharField(max_length=50, verbose_name='Title')),
40
                ('description', models.TextField(verbose_name='Description')),
41
                ('slug', models.SlugField(unique=True)),
42
                ('max_call_duration', models.PositiveIntegerField(default=120, verbose_name='Maximum duration of a call, in minutes.')),
43
                ('data_retention_period', models.PositiveIntegerField(default=60, verbose_name='Data retention period, in days.')),
44
                ('users', models.ManyToManyField(blank=True, to='base.ApiUser')),
45
            ],
46
            options={
47
                'verbose_name': 'Phone Calls',
48
            },
49
        ),
50
        migrations.AddField(
51
            model_name='call',
52
            name='resource',
53
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='phonecalls.PhoneCalls'),
54
        ),
55
    ]
passerelle/apps/phonecalls/models.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2019  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.db import models
18
from django.utils.timezone import datetime, timedelta, now, make_naive
19
from django.utils.translation import ugettext_lazy as _
20
from jsonfield import JSONField
21

  
22
from passerelle.base.models import BaseResource
23
from passerelle.utils.api import endpoint
24

  
25

  
26
class PhoneCalls(BaseResource):
27
    category = _('Telephony')
28

  
29
    max_call_duration = models.PositiveIntegerField(
30
            _('Maximum duration of a call, in minutes.'),
31
            help_text=_('Each hour, too long calls are closed.'),
32
            default=120)
33
    data_retention_period = models.PositiveIntegerField(
34
            _('Data retention period, in days.'),
35
            help_text=_('Each day, old calls are removed.'),
36
            default=60)
37

  
38
    class Meta:
39
        verbose_name = _('Phone Calls')
40

  
41
    @endpoint(name='call-start',
42
              perm='can_access',
43
              parameters={
44
                  'callee': {'description': _('Callee number'),
45
                             'example_value': '142'},
46
                  'caller': {'description': _('Caller number'),
47
                             'example_value': '0143350135'},
48
              })
49
    def call_start(self, request, callee, caller, **kwargs):
50
        # close current callee/caller calls
51
        Call.objects.filter(resource=self, callee=callee, caller=caller,
52
                            end_timestamp=None).update(end_timestamp=now())
53
        # create the new call
54
        new_call = Call(resource=self, callee=callee, caller=caller, details=kwargs)
55
        new_call.save()
56
        return {'data': new_call.json()}
57

  
58
    @endpoint(name='call-stop',
59
              perm='can_access',
60
              parameters={
61
                  'callee': {'description': _('Callee number'),
62
                             'example_value': '142'},
63
                  'caller': {'description': _('Caller number'),
64
                             'example_value': '0143350135'},
65
              })
66
    def call_stop(self, request, callee, caller, **kwargs):
67
        # close all current callee/caller calls, returns the last one
68
        current_calls = Call.objects.filter(resource=self, callee=callee, caller=caller,
69
                                            end_timestamp=None)
70
        current_calls_ids = list(current_calls.values_list('id', flat=True))
71
        current_calls.update(end_timestamp=now())
72
        # return current_calls with their new end_timestamp
73
        return {'data': [call.json() for call in Call.objects.filter(id__in=current_calls_ids)]}
74

  
75
    @endpoint(name='calls',
76
              perm='can_access',
77
              parameters={
78
                  'callee': {'description': _('Callee number'),
79
                             'example_value': '142'},
80
                  'limit': {'description': _('Maximal number of results')},
81
              })
82
    def calls(self, request, callee=None, caller=None, limit=30):
83
        calls = Call.objects.filter(resource=self)
84
        if callee:
85
            calls = calls.filter(callee=callee)
86
        if caller:
87
            calls = calls.filter(caller=caller)
88

  
89
        def json_list(calls):
90
            return [call.json() for call in calls[:limit]]
91
        return {
92
            'data': {
93
                'current': json_list(calls.filter(end_timestamp__isnull=True)),
94
                'past': json_list(calls.filter(end_timestamp__isnull=False)),
95
            }
96
        }
97

  
98
    def hourly(self):
99
        super(PhoneCalls, self).hourly()
100
        # close unfinished long calls
101
        maximal_time = now() - timedelta(minutes=self.max_call_duration)
102
        Call.objects.filter(resource=self, end_timestamp=None,
103
                            start_timestamp__lt=maximal_time).update(end_timestamp=now())
104

  
105
    def daily(self):
106
        super(PhoneCalls, self).daily()
107
        # remove finished old calls
108
        maximal_datetime = now() - timedelta(days=self.data_retention_period)
109
        Call.objects.filter(resource=self, end_timestamp__isnull=False,
110
                            end_timestamp__lt=maximal_datetime).delete()
111

  
112

  
113
class Call(models.Model):
114
    resource = models.ForeignKey(PhoneCalls)
115
    callee = models.CharField(blank=False, max_length=64)
116
    caller = models.CharField(blank=False, max_length=64)
117
    start_timestamp = models.DateTimeField(auto_now_add=True)
118
    end_timestamp = models.DateTimeField(null=True, default=None)
119
    details = JSONField(default={})
120

  
121
    class Meta:
122
        verbose_name = _('Phone Call')
123
        ordering = ['-start_timestamp']
124

  
125
    def json(self):
126
        # We use make_naive to send localtime, because this API will be used
127
        # by javascript, which it will not be comfortable with UTC datetimes
128
        if self.end_timestamp:
129
            current = False
130
            end_timestamp = make_naive(self.end_timestamp)
131
            duration = self.end_timestamp - self.start_timestamp
132
        else:
133
            current = True
134
            end_timestamp = None
135
            duration = now() - self.start_timestamp
136
        return {
137
            'caller': self.caller,
138
            'callee': self.callee,
139
            'start_timestamp': make_naive(self.start_timestamp),
140
            'end_timestamp': end_timestamp,
141
            'current': current,
142
            'duration': duration.seconds,
143
            'details': self.details,
144
        }
145

  
146
    def __repr__(self):
147
        return ('Call %(caller)s to %(callee)s - '
148
                'start %(start_timestamp)s, end %(end_timestamp)s') % self.json()
passerelle/settings.py
138 138
    'passerelle.apps.ovh',
139 139
    'passerelle.apps.oxyd',
140 140
    'passerelle.apps.pastell',
141
    'passerelle.apps.phonecalls',
141 142
    'passerelle.apps.solis',
142 143
    'passerelle.apps.arpege_ecp',
143 144
    # backoffice templates and static
144
-