Ver código fonte

Generalize CDR/CAL parser

Hal De 4 anos atrás
pai
commit
d6b1588e63
2 arquivos alterados com 53 adições e 80 exclusões
  1. 24 65
      app/app.py
  2. 29 15
      app/cel.py

+ 24 - 65
app/app.py

@@ -22,13 +22,13 @@ class ApiJsonEncoder(JSONEncoder):
   def default(self, o):
     if isinstance(o, dt):
       return o.isoformat()
-    if isinstance(o, CELEventChannel):
+    if isinstance(o, CdrChannel):
       return str(o)
-    if isinstance(o, CELEvent):
+    if isinstance(o, CdrEvent):
       return o.__dict__
-    if isinstance(o, CELEvents):
+    if isinstance(o, CdrEvents) or isinstance(o, CelEvents):
       return o.all
-    if isinstance(o, CELCall):
+    if isinstance(o, CdrCall) or isinstance(o, CelCall):
       return o.__dict__
     return JSONEncoder.default(self, o)
 
@@ -157,74 +157,33 @@ async def presenceStatusCallback(mngr: Manager, msg: Message):
     if combinedState != prevState:
       await userStateChangeCallback(user, combinedState, prevState)
 
-async def getCDR(start=None, end=None, **kwargs):
+async def getCDR(start=None, end=None, table='cdr', field='calldate'):
   _cdr = {}
   if end is None:
     end = dt.now()
   if start is None:
     start=(end - td(hours=24))
-  async for row in db.iterate(query='''SELECT linkedid,
-                                              uniqueid,
-                                              calldate,
-                                              did,
-                                              src,
-                                              dst,
-                                              clid,
-                                              dcontext,
-                                              channel,
-                                              dstchannel,
-                                              lastapp,
-                                              duration,
-                                              billsec,
-                                              disposition,
-                                              recordingfile,
-                                              cnum,
-                                              cnam,
-                                              outbound_cnum,
-                                              outbound_cnam,
-                                              dst_cnam,
-                                              peeraccount
-                                       FROM cdr
+  async for row in db.iterate(query='''SELECT *
+                                       FROM {table}
                                        WHERE linkedid
                                        IN (SELECT DISTINCT(linkedid)
-                                       FROM cdr
-                                       WHERE calldate
-                                       BETWEEN :start AND :end)
-                                       ORDER BY linkedid,
-                                                calldate,
-                                                uniqueid;''',
+                                       FROM {table}
+                                       WHERE {field}
+                                       BETWEEN :start AND :end);'''.format(table=table,
+                                                                           field=field),
                               values={'start':start,
                                       'end':end}):
-    event = {_k: str(_v) for _k, _v in row.items() if _k != 'linkedid' and _v != ''}
-    _cdr.setdefault(row['linkedid'],[]).append(event)
+    if row['linkedid'] in _cdr:
+      _cdr[row['linkedid']].events.add(row)
+    else:
+      _cdr[row['linkedid']]=CdrCall(row)
   cdr = []
   for _id in sorted(_cdr.keys()):
-    cdr.append({'id':_id,'events':_cdr[_id]})
+    cdr.append(_cdr[_id])
   return cdr
 
-async def getCEL(start=None, end=None, **kwargs):
-  _cel = {}
-  if end is None:
-    end = dt.now()
-  if start is None:
-    start=(end - td(hours=24))
-  async for row in db.iterate(query='''SELECT *
-                                       FROM cel
-                                       WHERE linkedid
-                                       IN (SELECT DISTINCT(linkedid)
-                                       FROM cel
-                                       WHERE eventtime
-                                       BETWEEN :start AND :end);''',
-                              values={'start':start,
-                                      'end':end}):
-    if row['linkedid'] in _cel:
-      _cel[row['linkedid']].events.add(row)
-    else:
-      _cel[row['linkedid']]=CELCall(row)
-  cel = []
-  for _id in sorted(_cel.keys()):
-    cel.append(_cel[_id])
-  return cel
+async def getCEL(start=None, end=None, table='cel', field='eventtime'):
+  return await getCDR(start, end, table, field)
 
 @app.before_first_request
 async def initHttpClient():
@@ -809,8 +768,8 @@ class DeviceUnBind(Resource):
 
 @app.route('/cdr')
 class CDR(Resource):
-  @app.param('end', 'End of datetime range. Defaults to now. Allowed formats are: timestamp, ISO 8601 or yyyymmddHHMMSS', 'query')
-  @app.param('start', 'Start of datetime range. Defaults to end-24h. Allowed formats are: timestamp, ISO 8601 or yyyymmddHHMMSS', 'query')
+  @app.param('end', 'End of datetime range. Defaults to now. Allowed formats are: timestamp, ISO 8601 or YYYYMMDDhhmmss', 'query')
+  @app.param('start', 'Start of datetime range. Defaults to end-24h. Allowed formats are: timestamp, ISO 8601 or YYYYMMDDhhmmss', 'query')
   @app.response(HTTPStatus.OK, 'JSON reply')
   @app.response(HTTPStatus.UNAUTHORIZED, 'Authorization required')
   async def get(self):
@@ -824,8 +783,8 @@ class CDR(Resource):
 
 @app.route('/cel')
 class CEL(Resource):
-  @app.param('end', 'End of datetime range. Defaults to now. Allowed formats are: timestamp, ISO 8601 or yyyymmddHHMMSS', 'query')
-  @app.param('start', 'Start of datetime range. Defaults to end-24h. Allowed formats are: timestamp, ISO 8601 or yyyymmddHHMMSS', 'query')
+  @app.param('end', 'End of datetime range. Defaults to now. Allowed formats are: timestamp, ISO 8601 or YYYYMMDDhhmmss', 'query')
+  @app.param('start', 'Start of datetime range. Defaults to end-24h. Allowed formats are: timestamp, ISO 8601 or YYYYMMDDhhmmss', 'query')
   @app.response(HTTPStatus.OK, 'JSON reply')
   @app.response(HTTPStatus.UNAUTHORIZED, 'Authorization required')
   async def get(self):
@@ -839,8 +798,8 @@ class CEL(Resource):
 
 @app.route('/calls')
 class Calls(Resource):
-  @app.param('end', 'End of datetime range. Defaults to now. Allowed formats are: timestamp, ISO 8601 and yyyymmddHHMMSS', 'query')
-  @app.param('start', 'Start of datetime range. Defaults to end-24h. Allowed formats are: timestamp, ISO 8601 and yyyymmddHHMMSS', 'query')
+  @app.param('end', 'End of datetime range. Defaults to now. Allowed formats are: timestamp, ISO 8601 and YYYYMMDDhhmmss', 'query')
+  @app.param('start', 'Start of datetime range. Defaults to end-24h. Allowed formats are: timestamp, ISO 8601 and YYYYMMDDhhmmss', 'query')
   @app.response(HTTPStatus.OK, 'JSON reply')
   @app.response(HTTPStatus.UNAUTHORIZED, 'Authorization required')
   async def get(self):

+ 29 - 15
app/cel.py

@@ -3,9 +3,9 @@ import json
 import re
 from datetime import datetime
 
-fieldsFilter = ('id', 'linkedid', 'appdata')
+fieldsFilter = ('id', 'linkedid', 'appdata', 'lastdata')
 
-class CELEventChannel:
+class CdrChannel:
   def __init__(self, chanData):
     self._str = chanData
     (self.tech,
@@ -16,25 +16,25 @@ class CELEventChannel:
   def __repr__(self):
     return self._str
 
-class CELEvent:
+class CdrEvent:
   def __init__(self, eventData):
     for key, value in eventData.items():
       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':
-          setattr(self, key, CELEventChannel(value))
+          setattr(self, key, CdrChannel(value))
         else:
           setattr(self, key, value)
 
-class CELEvents:
+class CdrEvents:
   def __init__(self):
     self._events = []
   def add(self, event):
-    if isinstance(event, CELEvent):
+    if isinstance(event, CdrEvent):
       self._events.append(event)
     else:
-      self._events.append(CELEvent(event))
+      self._events.append(CdrEvent(event))
   @property
   def all(self):
     return self._events
@@ -48,14 +48,12 @@ class CELEvents:
   def last(self):
     return self._events[-1] if len(self._events) > 0 else None
   def filter(self, key, value, func='__eq__'):
-    ''' events.filter('channame','PJSIP/','startswith')
-    '''
-    result = CELEvents()
+    result = CdrEvents()
     for event in self._events:
       if getattr(str(getattr(event, key)), func)(value):
         result.add(event)
     return result
-  def has(self, value, key='eventtype'):
+  def has(self, value, key='disposition'):
     for event in self._events:
       if getattr(event, key) == value:
         return True
@@ -63,9 +61,13 @@ class CELEvents:
   def __len__(self):
     return len(self._events)
 
-class CELCall:
+class CelEvents(CdrEvents):
+  def has(self, value, key='eventtype'):
+    return super().has(value, key)
+
+class CdrCall:
   def __init__(self, event=None):
-    self.events = CELEvents()
+    self.events = CdrEvents()
     if event is not None:
       self.events.add(event)
       self.linkedid = event['linkedid']
@@ -73,7 +75,19 @@ class CELCall:
       self.linkedid = None
   @property
   def start(self):
-    return self.events.first.eventtime
+    return self.events.first.calldate
   @property
   def isAnswered(self):
-    return self.events.has('BRIDGE_ENTER');
+    return self.events.first.disposition == 'ANSWERED';
+
+class CelCall:
+  def __init__(self, event=None):
+    self.events = CelEvents()
+    if event is not None:
+      self.events.add(event)
+      self.linkedid = event['linkedid']
+    else:
+      self.linkedid = None
+  @property
+  def start(self):
+    return self.events.first.eventtime