templates_people_DATA = \
src/templates/people/conferences.html \
- src/templates/people/call.html \
- src/templates/people/calls.html \
src/templates/people/index.html \
src/templates/people/sip.html \
src/templates/people/subscribed.html \
templates_people_modules_DATA = \
src/templates/people/modules/agent.html \
- src/templates/people/modules/cdr.html \
src/templates/people/modules/channels.html \
- src/templates/people/modules/mos.html \
src/templates/people/modules/password.html \
src/templates/people/modules/password.js \
src/templates/people/modules/registrations.html
return await self.backend.asterisk.get_sip_channels(self.sip_id)
- def get_cdr(self, date=None, limit=None):
- return self.backend.talk.freeswitch.get_cdr_by_account(self, date=date, limit=limit)
-
# Phone Numbers
@lazy_property
"inbound", account.sip_id, "outbound", account.sip_id,
account._all_telephone_numbers, "DOWN")
- def get_cdr_by_account(self, account, date=None, limit=None):
- res = self.db.query("SELECT * FROM cdr \
- WHERE ((caller_id_number = ANY(%s) AND bleg_uuid IS NOT NULL) \
- OR (destination_number = ANY(%s) AND bleg_uuid IS NULL)) \
- AND (%s IS NULL OR start_stamp::date = %s) \
- ORDER BY end_stamp DESC LIMIT %s", account._all_telephone_numbers,
- account._all_telephone_numbers, date, date, limit)
-
- for row in res:
- yield CDR(self, data=row)
-
- def get_call_by_uuid(self, uuid):
- res = self.db.get("SELECT * FROM cdr \
- WHERE uuid = %s", uuid)
-
- if res:
- return CDR(self, data=res)
-
def get_conferences(self):
res = self.db.query("SELECT DISTINCT application_data AS handle FROM channels \
WHERE application = %s AND application_data LIKE %s \
return "%s: %s" % (key_negotiation.upper(), cipher_suite.replace("_", "-"))
-class CDR(object):
- def __init__(self, freeswitch, data):
- self.freeswitch = freeswitch
- self.data = data
-
- @property
- def backend(self):
- return self.freeswitch.backend
-
- @property
- def db(self):
- return self.freeswitch.db
-
- @property
- def uuid(self):
- return self.data.uuid
-
- @lazy_property
- def bleg(self):
- if self.data.bleg_uuid:
- return self.freeswitch.get_call_by_uuid(self.data.bleg_uuid)
-
- # If we are the bleg, we need to search for one where UUID is the bleg
- res = self.db.get("SELECT * FROM cdr WHERE bleg_uuid = %s", self.uuid)
-
- if res:
- return CDR(self.freeswitch, data=res)
-
- @property
- def direction(self):
- if self.data.bleg_uuid:
- return "inbound"
-
- return "outbound"
-
- @lazy_property
- def caller(self):
- return self.backend.accounts.get_by_phone_number(self.data.caller_id_number)
-
- @property
- def caller_number(self):
- return self.data.caller_id_number
-
- @lazy_property
- def callee(self):
- return self.backend.accounts.get_by_phone_number(self.data.destination_number)
-
- @property
- def callee_number(self):
- return self.data.destination_number
-
- @property
- def time_start(self):
- return self.data.start_stamp
-
- @property
- def time_answered(self):
- return self.data.answer_stamp
-
- @property
- def duration(self):
- return self.data.duration
-
- @property
- def codec(self):
- return format_codec(self.data.write_codec, int(self.data.write_rate or 0), int(self.data.write_bit_rate or 0))
-
- @property
- def user_agent(self):
- if self.data.user_agent:
- return self.data.user_agent.replace("_", " ")
-
- @property
- def size(self):
- return sum((self.data.rtp_audio_in_raw_bytes or 0, self.data.rtp_audio_out_raw_bytes or 0))
-
- @property
- def mos(self):
- return self.data.rtp_audio_in_mos
-
-
class Conference(object):
def __init__(self, freeswitch, handle):
self.freeswitch = freeswitch
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }} - {{ _("Call") }}{% end block %}
-
-{% block container %}
- <div class="header">
- <div class="container">
- <h1>
- {% if call.direction == "inbound" %}
- {{ _("Call to") }}
-
- {% if call.callee %}
- <a href="/users/{{ call.callee.uid }}">{{ call.callee }}</a>
- {% else %}
- {{ format_phone_number(call.callee_number) }}
- {% end %}
- {% elif call.direction == "outbound" %}
- {{ _("Call from") }}
-
- {% if call.caller %}
- <a href="/users/{{ call.caller.uid }}">{{ call.caller }}</a>
- {% else %}
- {{ format_phone_number(call.caller_number) }}
- {% end %}
- {% end %}
- </h1>
-
- <ul class="list-inline text-muted">
- <li class="list-inline-item">
- {{ locale.format_date(call.time_answered or call.time_start, relative=False) }}
- </li>
-
- <li class="list-inline-item">
- {% if call.duration %}
- {{ format_time(call.duration) }}
- {% else %}
- {{ _("Not Answered") }}
- {% end %}
- </li>
- </p>
- </div>
- </div>
-
- <div class="container">
- <section>
- <h4>{{ _("Media Information") }}</h4>
-
- <div class="row">
- {% for c in (call, call.bleg) %}
- {% if c %}
- <div class="col-12 col-lg-6 mb-4">
- <div class="card">
- <div class="card-body">
- <h6 class="card-title">
- {% if c == call %}
- {{ _("Your Leg") }}
- {% else %}
- {{ _("Other Leg") }}
- {% end %}
-
- {% if c.user_agent %}
- <small class="text-muted">
- ({{ c.user_agent }})
- </small>
- {% end %}
- </h6>
-
-
-
- <p class="text-center">
- {% module MOS(c) %}
- </p>
-
- <ul class="list-inline text-center small mb-0">
- {% if c.codec %}
- <li class="list-inline-item text-muted">
- {{ c.codec }}
- </li>
- {% end %}
-
- {% if c.size %}
- <li class="list-inline-item text-muted">
- {{ format_size(c.size) }}
- </li>
- {% end %}
- </ul>
- </div>
- </div>
- </div>
- {% end %}
- {% end %}
- </div>
- </section>
- </div>
-{% end %}
+++ /dev/null
-{% extends "../base.html" %}
-
-{% block title %}{{ account }} - {{ _("Calls") }}{% end block %}
-
-{% block container %}
- <div class="header">
- <div class="container">
- <h1>{{ _("Calls") }}</h1>
-
- <p class="text-muted">
- {{ locale.format_day(date) }}
- </p>
- </div>
- </div>
-
- <div class="container">
- <section>
- {% module Channels(account) %}
-
- <nav>
- <ul class="pagination justify-content-center">
- {% set yesterday = date - datetime.timedelta(days=1) %}
- {% set tomorrow = date + datetime.timedelta(days=1) %}
-
- <li class="page-item">
- <a class="page-link" href="/users/{{ account.uid }}/calls/{{ yesterday }}">
- « {{ locale.format_day(yesterday) }}
- </a>
- </li>
-
- <li class="page-item {% if tomorrow > now %}disabled{% end %}">
- <a class="page-link" href="/users/{{ account.uid }}/calls/{{ tomorrow }}">
- {{ locale.format_day(tomorrow) }} »
- </a>
- </li>
- </ul>
- </nav>
-
- {% module CDR(account, date=date) %}
- </section>
- </div>
-{% end block %}
+++ /dev/null
-{% if cdr %}
- <div class="list-group">
- {% for c in cdr %}
- <a class="list-group-item list-group-item-action"
- href="/users/{{ account.uid }}/calls/{{ c.uuid }}">
- {% if c.direction == "inbound" %}
- <span class="fas fa-arrow-right text-danger"></span>
-
- {% if c.callee %}
- {{ c.callee }}
- {% else %}
- {{ format_phone_number(c.callee_number) }}
- {% end %}
- {% elif c.direction == "outbound" %}
- <span class="fas fa-arrow-left text-success"></span>
-
- {% if c.caller %}
- {{ c.caller }}
- {% else %}
- {{ format_phone_number(c.caller_number) }}
- {% end %}
- {% end %}
-
- <ul class="list-inline small mb-0">
- <li class="list-inline-item">
- {{ locale.format_date(c.time_start, relative=False) }}
- </li>
-
- <li class="list-inline-item">
- {% module MOS(c) %}
- </li>
-
- {% if c.time_answered %}
- <li class="list-inline-item text-muted">
- {{ format_time(c.duration) }}
- </li>
- {% else %}
- <li class="list-inline-item text-danger">
- {{ _("Not Answered") }}
- </li>
- {% end %}
- </ul>
- </a>
- {% end %}
- </div>
-{% else %}
- <p class="text-muted text-center my-5">
- {{ _("There are no calls") }}
- </p>
-{% end %}
+++ /dev/null
-{% if call.mos %}
- {% if call.mos >= 4.5 %}
- {% set color = "text-success" %}
- {% elif call.mos >= 4.3 %}
- {% set color = "text-warning" %}
- {% else %}
- {% set color = "text-danger" %}
- {% end %}
-
- <span class="text-muted" title="{{ "%.2f" % call.mos }}/5">
- {% for i in range(5) %}
- {% if call.mos > (i + 0.5) %}
- <span class="fas fa-star {{ color }}"></span>
- {% elif call.mos > i %}
- <span class="fas fa-star-half-alt {{ color }}"></span>
- {% else %}
- <span class="far fa-star {{ color }}"></span>
- {% end %}
- {% end %}
- </span>
-{% end %}
{% end %}
{% if account.can_be_managed_by(current_user) %}
- {% if account.has_sip() %}
- <a class="button is-success" href="/users/{{ account.uid }}/calls">
- <span class="icon-text">
- <span class="icon">
- <i class="fas fa-phone"></i>
- </span>
- <span>{{ _("Calls") }}</span>
- </span>
- </a>
- {% end %}
-
<a class="button is-light" href="/users/{{ account.uid }}/passwd">
{{ _("Change Password") }}
</a>
# People
"Agent" : people.AgentModule,
- "CDR" : people.CDRModule,
"Channels" : people.ChannelsModule,
- "MOS" : people.MOSModule,
"Password" : people.PasswordModule,
"Registrations" : people.RegistrationsModule,
(r"/activate/([a-z_][a-z0-9_-]{0,31})/(\w+)", auth.ActivateHandler),
(r"/conferences", people.ConferencesHandler),
(r"/register", auth.RegisterHandler),
- (r"/users/([a-z_][a-z0-9_-]{0,31})/calls/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})", people.CallHandler),
- (r"/users/([a-z_][a-z0-9_-]{0,31})/calls(?:/(\d{4}-\d{2}-\d{2}))?", people.CallsHandler),
(r"/users/([a-z_][a-z0-9_-]{0,31})/sip", people.SIPHandler),
# Promotional Consent Stuff
self.render("people/index.html")
-class CallsHandler(base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid, date=None):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Check for permissions
- if not account.can_be_managed_by(self.current_user):
- raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
-
- if date:
- try:
- date = datetime.datetime.strptime(date, "%Y-%m-%d").date()
- except ValueError:
- raise tornado.web.HTTPError(400, "Invalid date: %s" % date)
- else:
- date = datetime.date.today()
-
- self.render("people/calls.html", account=account, date=date)
-
-
-class CallHandler(base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uid, uuid):
- account = self.backend.accounts.get_by_uid(uid)
- if not account:
- raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
-
- # Check for permissions
- if not account.can_be_managed_by(self.current_user):
- raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
-
- call = self.backend.talk.freeswitch.get_call_by_uuid(uuid)
- if not call:
- raise tornado.web.HTTPError(404, "Could not find call %s" % uuid)
-
- # XXX limit
-
- self.render("people/call.html", account=account, call=call)
-
-
class ConferencesHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self):
return self.render_string("people/modules/agent.html", account=account)
-class CDRModule(ui_modules.UIModule):
- def render(self, account, date=None, limit=None):
- cdr = account.get_cdr(date=date, limit=limit)
-
- return self.render_string("people/modules/cdr.html",
- account=account, cdr=list(cdr))
-
-
class ChannelsModule(ui_modules.UIModule):
def render(self, account):
return self.render_string("people/modules/channels.html",
account=account, channels=account.sip_channels)
-class MOSModule(ui_modules.UIModule):
- def render(self, call):
- return self.render_string("people/modules/mos.html", call=call)
-
-
class PasswordModule(ui_modules.UIModule):
def render(self, account=None):
return self.render_string("people/modules/password.html", account=account)