Преглед на файлове

Aggregated calls draft implementation

Hal De преди 4 години
родител
ревизия
f377eb4e4b
променени са 3 файла, в които са добавени 67 реда и са изтрити 55 реда
  1. 14 41
      app/app.py
  2. 53 2
      app/cel.py
  3. 0 12
      app/utils.py

+ 14 - 41
app/app.py

@@ -161,7 +161,7 @@ async def getCDR(start=None,
                  end=None,
                  table='cdr',
                  field='calldate',
-                 sort='CAST(linkedid AS DECIMAL(12,2)), CAST(uniqueid AS DECIMAL(12,2)), sequence'):
+                 sort='calldate, SUBSTR(uniqueid,1,10), SUBSTR(uniqueid,12,2), sequence'):
   _cdr = {}
   if end is None:
     end = dt.now()
@@ -809,52 +809,25 @@ class Calls(Resource):
   @app.response(HTTPStatus.OK, 'JSON reply')
   @app.response(HTTPStatus.UNAUTHORIZED, 'Authorization required')
   async def get(self):
-    '''Returns aggregated call data JSON. NOT IMPLEMENTED.
+    '''Returns aggregated call data JSON. Draft implementation.
     All request arguments are optional.
     '''
     calls = []
     start = parseDatetime(request.args.get('start'))
     end = parseDatetime(request.args.get('end'))
     cdr = await getCDR(start, end)
-    for _call in cdr:
-      _call0 = _call['events'][0]
-      dcontext = _call0['dcontext']
-      call = {'id':_call['id'],
-              'start':_call0['calldate'],
-              'type': None,
-              'numberA': None,
-              'numberB': None,
-              'line': None,
-              'duration': None,
-              'waiting': None,
-              'status':'NO ANSWER',
-              'url': None }
-      for _c, _r in (('disposition','status'),
-                     ('src','numberA'),
-                     ('recordingfile','url')):
-        if _c in  _call0:
-          call[_r] = _call0[_c]
-      # if context in ('from-internal'):
-
-      # if context in ('ext-queues'):
-        # call['type'] = 'in'
-        # if 'did' in _call0:
-          # call['line'] = _call0['did']
-
-
-          # call['type'] = 'local'
-        # else:
-          # call['type'] = 'out'
-          # call['numberB'] = _call0['dst']
-      # else:
-        # call['type'] = 'in'
-        # if 'did' in _call0:
-          # call['line'] = _call0['did']
-      # if len(_call['events']) > 1:
-        # for step in _call['events'][1:]:
-
-          # pass
-      calls.append(call)
+    for c in cdr:
+      _call = {'id':c.linkedid,
+               'start':c.start,
+               'type': c.direction,
+               'numberA': c.src,
+               'numberB': c.dst,
+               'line': c.did,
+               'duration': c.duration,
+               'waiting': c.waiting,
+               'status':c.disposition,
+               'url': c.file }
+      calls.append(_call)
     return successReply(calls)
 
 manager.connect()

+ 53 - 2
app/cel.py

@@ -22,10 +22,12 @@ class CdrEvent:
       if ((key not in fieldsFilter) and (value is not None) and (str(value) != '')):
         if key == 'extra':
           setattr(self, key, json.loads(value))
-        elif key == 'channame':
+        elif key in ('channame','channel','dstchannel'):
           setattr(self, key, CdrChannel(value))
         else:
           setattr(self, key, value)
+  def __getattr__(self, attr):
+    return None
 
 class CdrEvents:
   def __init__(self):
@@ -77,8 +79,57 @@ class CdrCall:
   def start(self):
     return self.events.first.calldate
   @property
+  def disposition(self):
+    return self.events.first.disposition
+  @property
+  def file(self):
+    return self.events.first.recordingfile
+  @property
+  def src(self):
+    return self.events.first.cnum
+  @property
+  def direction(self):
+    if self.events.first.dcontext == 'from-internal':
+      if self.events.first.outbound_cnum is not None:
+        return 'outbound'
+      else:
+        return 'local'
+    else:
+      return 'inbound'
+  @property
+  def dst(self):
+    # placeholder
+    # TODO: determine last dst based on disposition
+    return self.events.last.dst
+  @property
+  def did(self):
+    if self.direction == 'inbound':
+      return self.events.first.did
+    else:
+      return None
+  @property
+  def duration(self):
+    if not self.isAnswered:
+      return 0
+    else:
+      _t = 0
+      if len(self.events) > 1:
+        for event in self.events.all[1:]:
+          if event.disposition == 'ANSWERED':
+            _t = _t + event.billsec
+      else:
+        _t = self.events.first.billsec
+      return _t
+  @property
+  def waiting(self):
+    if not self.isAnswered:
+      return self.events.first.duration
+    else:
+      # TODO: exclude time on ivr
+      return self.events.first.duration - self.duration
+  @property
   def isAnswered(self):
-    return self.events.first.disposition == 'ANSWERED';
+    return self.disposition == 'ANSWERED';
 
 class CelCall:
   def __init__(self, event=None):

+ 0 - 12
app/utils.py

@@ -6,10 +6,6 @@ from datetime import timedelta as td
 
 TRUEs = ('true', '1', 'y', 'yes')
 NONEs = (None,'none','')
-externalContexts = ('from-trunk',
-                    'from-digital',
-                    'from-pstn',
-                    'from-did-direct')
 # userstate presencestate maping:
 _ustates = ['idle',       'inuse', 'busy', 'unavailable', 'ringing', 'inuse&ringing','hold', 'inuse&hold'] #presence:
 _states = [['available',   'busy', 'busy', 'unavailable', 'ringing', 'busy',         'busy', 'busy'],      #not_set
@@ -75,14 +71,6 @@ def parseDatetime(dateString):
         pass
   return _dt
 
-def freepbxExternalContext(context):
-  if ((context in externalContexts) or
-      (context.startswith('from-trunk-')) or
-      (context.startswith('from-did-')) or
-      (context.startswith('ivr-'))):
-    return True
-  return False
-
 def followMe2DevState(followMeState):
   if followMeState == 'DIRECT':
     return 'INUSE'