cel.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. #!/usr/bin/env python3
  2. import json
  3. import re
  4. from datetime import datetime
  5. fieldsFilter = ('id', 'linkedid', 'appdata', 'lastdata')
  6. class CdrChannel:
  7. def __init__(self, chanData):
  8. self._str = chanData
  9. (self.tech,
  10. self.peer,
  11. self.context,
  12. self.id,
  13. self.leg) = re.match(r'(\w+)/(\w+)(?:@([\w-]+))?-([0-9a-f]{8})(?:;([0-9]+))?', chanData).groups()
  14. def __repr__(self):
  15. return self._str
  16. class CdrEvent:
  17. def __init__(self, eventData):
  18. for key, value in eventData.items():
  19. if ((key not in fieldsFilter) and (value is not None) and (str(value) != '')):
  20. if key == 'extra':
  21. setattr(self, key, json.loads(value))
  22. elif key in ('channame','channel','dstchannel'):
  23. setattr(self, key, CdrChannel(value))
  24. else:
  25. setattr(self, key, value)
  26. def __getattr__(self, attr):
  27. return None
  28. class CdrEvents:
  29. def __init__(self):
  30. self._events = []
  31. def add(self, event):
  32. if isinstance(event, CdrEvent):
  33. self._events.append(event)
  34. else:
  35. self._events.append(CdrEvent(event))
  36. @property
  37. def all(self):
  38. return self._events
  39. @property
  40. def first(self):
  41. return self._events[0] if len(self._events) > 0 else None
  42. @property
  43. def second(self):
  44. return self._events[1] if len(self._events) > 1 else None
  45. @property
  46. def last(self):
  47. return self._events[-1] if len(self._events) > 0 else None
  48. def filter(self, key, value, func='__eq__'):
  49. result = CdrEvents()
  50. for event in self._events:
  51. if getattr(str(getattr(event, key)), func)(value):
  52. result.add(event)
  53. return result
  54. def has(self, value, key='disposition'):
  55. for event in self._events:
  56. if getattr(event, key) == value:
  57. return True
  58. return False
  59. def __len__(self):
  60. return len(self._events)
  61. class CelEvents(CdrEvents):
  62. def has(self, value, key='eventtype'):
  63. return super().has(value, key)
  64. class CdrCall:
  65. def __init__(self, event=None):
  66. self.events = CdrEvents()
  67. if event is not None:
  68. self.events.add(event)
  69. self.linkedid = event['linkedid']
  70. else:
  71. self.linkedid = None
  72. @property
  73. def start(self):
  74. return self.events.first.calldate
  75. @property
  76. def disposition(self):
  77. return self.events.first.disposition
  78. @property
  79. def file(self):
  80. return self.events.first.recordingfile
  81. @property
  82. def src(self):
  83. return self.events.first.cnum
  84. @property
  85. def direction(self):
  86. if self.events.first.dcontext == 'from-internal':
  87. if self.events.first.outbound_cnum is not None:
  88. return 'outbound'
  89. else:
  90. return 'local'
  91. else:
  92. return 'inbound'
  93. @property
  94. def dst(self):
  95. # placeholder
  96. # TODO: determine last dst based on disposition
  97. if self.direction == 'outbound':
  98. return self.events.first.dst
  99. else:
  100. if self.events.first.dstchannel is not None:
  101. return self.events.first.dstchannel.peer
  102. else:
  103. return self.events.first.dst
  104. @property
  105. def did(self):
  106. if self.direction == 'inbound':
  107. return self.events.first.did
  108. else:
  109. return None
  110. @property
  111. def duration(self):
  112. if not self.isAnswered:
  113. return 0
  114. else:
  115. if len(self.events) > 1:
  116. return self.events.second.billsec
  117. else:
  118. return self.events.first.billsec
  119. @property
  120. def waiting(self):
  121. if not self.isAnswered:
  122. return self.events.first.duration
  123. else:
  124. # TODO: exclude time on ivr
  125. return self.events.first.duration - self.duration
  126. @property
  127. def isAnswered(self):
  128. return self.disposition == 'ANSWERED';
  129. class CelCall:
  130. def __init__(self, event=None):
  131. self.events = CelEvents()
  132. if event is not None:
  133. self.events.add(event)
  134. self.linkedid = event['linkedid']
  135. else:
  136. self.linkedid = None
  137. @property
  138. def start(self):
  139. return self.events.first.eventtime