From 77431b9ccdf1d70100e9d59b759d84d189488abf Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sun, 19 Apr 2015 13:14:54 +0200 Subject: [PATCH] talk: Update for Kamailio --- templates/modules/menu.html | 10 +- templates/talk/diagnosis.html | 10 +- templates/talk/index.html | 10 +- templates/talk/modules/call-log.html | 36 +++--- templates/talk/modules/lines.html | 40 +++++++ templates/talk/modules/ongoing-calls.html | 2 + templates/talk/phonebook-contact.html | 40 +------ templates/talk/trunks.html | 33 ------ webapp/__init__.py | 2 +- webapp/backend/talk.py | 129 ++++++++++++++-------- webapp/handlers_talk.py | 24 +--- webapp/ui_modules.py | 44 +++++++- 12 files changed, 209 insertions(+), 171 deletions(-) create mode 100644 templates/talk/modules/lines.html delete mode 100644 templates/talk/trunks.html diff --git a/templates/modules/menu.html b/templates/modules/menu.html index 66df0d2f..a36f338b 100644 --- a/templates/modules/menu.html +++ b/templates/modules/menu.html @@ -167,11 +167,11 @@ {{ _("Conferences") }} - {% if current_user and current_user.is_admin() %} -
  • - {{ _("Trunks") }} -
  • - {% end %} +
  • + + {{ _("Documentation") }} + +
  • {{ _("Diagnosis") }} diff --git a/templates/talk/diagnosis.html b/templates/talk/diagnosis.html index a56ff244..a609e7ba 100644 --- a/templates/talk/diagnosis.html +++ b/templates/talk/diagnosis.html @@ -4,11 +4,11 @@ {% block body %}
    -

    {{ _("Test Call") }}

    +

    {{ _("Test Call") }}

    @@ -16,4 +16,10 @@ - {{ _("A music playing service") }}
    + + {% if current_user.is_admin() %} +
    + {% module TalkLines(show_account=True) %} +
    + {% end %} {% end block %} diff --git a/templates/talk/index.html b/templates/talk/index.html index 1c79b185..e2bed372 100644 --- a/templates/talk/index.html +++ b/templates/talk/index.html @@ -44,13 +44,7 @@ {% end %} - {% if ongoing_calls %} -

    {{ _("Ongoing Calls") }}

    - {% module TalkOngoingCalls(ongoing_calls) %} - {% end %} + {% module TalkOngoingCalls(current_user) %} - {% if call_log %} -

    {{ _("Call Log") }}

    - {% module TalkCallLog(call_log) %} - {% end %} + {% module TalkCallLog(current_user) %} {% end block %} diff --git a/templates/talk/modules/call-log.html b/templates/talk/modules/call-log.html index d3e8b5ac..b39a139b 100644 --- a/templates/talk/modules/call-log.html +++ b/templates/talk/modules/call-log.html @@ -1,3 +1,5 @@ +

    {{ _("Call Log") }}

    + @@ -8,16 +10,16 @@ {% for entry in calls %} - {% if entry.status == "ringing" and entry.reason == "hangup" %} - - {% elif entry.status == "ringing" and entry.reason == "Request Terminated" %} - - {% else %} + {% if entry.sip_code == "200" %} + {% elif entry.caller == viewer.sip_id %} + + {% else %} + {% end %} diff --git a/templates/talk/modules/lines.html b/templates/talk/modules/lines.html new file mode 100644 index 00000000..722ee13c --- /dev/null +++ b/templates/talk/modules/lines.html @@ -0,0 +1,40 @@ +

    {{ _("Lines") }}

    + +
    {{ locale.format_date(entry.time, full_format=True, relative=False) }} - {% if entry.direction == "incoming" %} + {% if entry.called == viewer.sip_id %} {% if entry.caller_account %} {{ entry.caller_account.name }} @@ -25,7 +27,7 @@ {% else %} {{ entry.caller }} {% end %} - {% elif entry.direction == "outgoing" %} + {% elif entry.caller == viewer.sip_id %} {% if entry.called_account %} {{ entry.called_account.name }} @@ -36,14 +38,20 @@ {% end %} - {% if entry.status == "ringing" %} - {% if entry.reason == "hangup" %} - {{ _("no answer") }} - {% elif entry.reason == "Request Terminated" %} - {{ _("missed call") }} - {% end %} + {% if entry.sip_code == "200" %} + {{ entry.duration }} {% else %} - {{ entry.duration.strftime("%H:%M:%S") }} + {% if entry.reason == "Busy Here" %} + {{ _("Busy") }} + {% elif entry.reason in ("Cancelled", "Request Cancelled", "Request Timeout") %} + {% if entry.caller == viewer.sip_id %} + {{ _("no answer") }} + {% else %} + {{ _("missed call") }} + {% end %} + {% else %} + {{ entry.sip_code }} {{ entry.reason }} + {% end %} {% end %}
    + + + {% if show_account %} + + {% end %} + + + + + + + {% for line in lines %} + + {% if show_account %} + + {% end %} + + + + + + {% end %} + +
    {{ _("Account") }}{{ _("Expires") }}{{ _("Location") }}{{ _("User Agent") }}
    + {% if line.account %} + {{ line.account.name }} + ({{ line.account.sip_id }}) + + {% else %} + {{ line.username }}@{{ line.domain }} + {% end %} + {{ locale.format_date(line.expires) }} + {% if line.tls_enabled %} + + {% end %} + {{ line.location }} + {{ line.user_agent }}
    diff --git a/templates/talk/modules/ongoing-calls.html b/templates/talk/modules/ongoing-calls.html index 885b42b4..59b46832 100644 --- a/templates/talk/modules/ongoing-calls.html +++ b/templates/talk/modules/ongoing-calls.html @@ -1,3 +1,5 @@ +

    {{ _("Ongoing Calls") }}

    + diff --git a/templates/talk/phonebook-contact.html b/templates/talk/phonebook-contact.html index 7318e3c6..1fa535e3 100644 --- a/templates/talk/phonebook-contact.html +++ b/templates/talk/phonebook-contact.html @@ -96,41 +96,9 @@ {% end %} - {% if lines %} -

    {{ _("Lines") }}

    - -
    - - - - - - - - - {% for line in lines %} - - - - - - {% end %} - -
    {{ _("Expires") }}{{ _("Location") }}{{ _("User Agent") }}
    {{ locale.format_date(line.expires) }} - {% if line.tls_enabled %} - - {% end %} - {{ line.location }} - {{ line.user_agent }}
    - {% end %} - - {% if ongoing_calls %} -

    {{ _("Ongoing Calls") }}

    - {% module TalkOngoingCalls(ongoing_calls) %} - {% end %} - - {% if call_log %} -

    {{ _("Call Log") }}

    - {% module TalkCallLog(call_log) %} + {% if current_user == account or current_user.is_admin() %} + {% module TalkLines(account) %} + {% module TalkOngoingCalls(account) %} + {% module TalkCallLog(account) %} {% end %} {% end block %} diff --git a/templates/talk/trunks.html b/templates/talk/trunks.html deleted file mode 100644 index bfa9a912..00000000 --- a/templates/talk/trunks.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "../base.html" %} - -{% block title %}{{ _("Trunks") }}{% end block %} - -{% block body %} -

    {{ _("Trunks") }}

    - - - - - - - - - - {% for trunk in trunks %} - - - - - - {% end %} - -
    {{ _("Name") }}{{ _("Registrar") }}
    - {% if trunk.status == "online" %} - - {% elif trunk.status == "offline" or trunk.status is None %} - - {% else %} - {{ trunk.status }} - {% end %} - {{ trunk.description or trunk.account }}{{ trunk.username }}@{{ trunk.registrar }}
    -{% end block %} diff --git a/webapp/__init__.py b/webapp/__init__.py index de574b91..a8d78163 100644 --- a/webapp/__init__.py +++ b/webapp/__init__.py @@ -61,6 +61,7 @@ class Application(tornado.web.Application): "FireinfoDeviceAndGroupsTable" : FireinfoDeviceAndGroupsTableModule, "FireinfoGeoTable" : FireinfoGeoTableModule, "TalkCallLog" : TalkCallLogModule, + "TalkLines" : TalkLinesModule, "TalkOngoingCalls" : TalkOngoingCallsModule, "TrackerPeerList" : TrackerPeerListModule, "Wish" : WishModule, @@ -255,7 +256,6 @@ class Application(tornado.web.Application): (r"/phonebook/(\w+)", TalkPhonebookAccountHandler), (r"/phonebook", TalkPhonebookHandler), (r"/profile", TalkProfileHandler), - (r"/trunks", TalkTrunksHandler), ] + authentication_handlers + static_handlers) # accounts.ipfire.org diff --git a/webapp/backend/talk.py b/webapp/backend/talk.py index 70a41724..cad51d98 100644 --- a/webapp/backend/talk.py +++ b/webapp/backend/talk.py @@ -1,5 +1,7 @@ #!/usr/bin/python +import re + import database from misc import Object @@ -8,7 +10,7 @@ class Talk(Object): def init(self): db_args = self.backend.db._db_args db_args.update({ - "database" : "yate", + "database" : "kamailio", }) self._db = database.Connection(**db_args) @@ -31,7 +33,7 @@ class Talk(Object): return accounts def user_is_online(self, sip_id): - res = self.db.get("SELECT 1 FROM lines WHERE username = %s \ + res = self.db.get("SELECT 1 FROM location WHERE username = %s \ AND expires >= NOW() LIMIT 1", sip_id) if res: @@ -39,28 +41,35 @@ class Talk(Object): return False - def get_trunks(self): - res = self.db.query("SELECT * FROM accounts WHERE enabled = TRUE \ - ORDER BY description, account") - - return res + def get_lines(self, account=None): + accounts_cache = {} - def get_lines(self, account): - if not account.is_talk_enabled(): + if account and not account.is_talk_enabled(): return [] - res = self.db.query("SELECT location, expires, user_agent FROM lines \ - WHERE username = %s ORDER BY expires DESC", account.sip_id) + if account: + res = self.db.query("SELECT contact AS location, expires, user_agent, socket \ + FROM location WHERE domain = %s AND username = %s ORDER BY expires DESC", + "ipfire.org", account.sip_id) + else: + res = self.db.query("SELECT username, domain, contact AS location, \ + expires, user_agent, socket FROM location ORDER BY username, expires DESC") result = [] for row in res: - # Check if TLS is used - row.tls_enabled = row.location.startswith("sip/sips:") or \ - "transport=TLS" in row.location + if account: + row.account = account + elif row.username: + try: + row.account = accounts_cache[row.username] + except KeyError: + row.account = accounts_cache[row.username] = \ + self.accounts.get_by_sip_id(row.username) + else: + row.account = None - # Strip a leading sip/ - if row.location.startswith("sip/"): - row.location = row.location[4:] + # Check if TLS is used + row.tls_enabled = row.socket.startswith("tls:") # Cut off everything after ; row.location, sep, rest = row.location.partition(";") @@ -69,39 +78,62 @@ class Talk(Object): return result - def _process_cdr(self, entries): + def _process_sip_uri(self, sip_uri): + sip_uri, delimiter, rest = sip_uri.partition(";") + + m = re.match(r"^sip:([0-9]{4})@ipfire\.org", sip_uri) + if m: + return m.group(1) + + # Remove trailing default port + if sip_uri.endswith(":5060"): + sip_uri = sip_uri.replace(":5060", "") + + return sip_uri + + def _process_cdr(self, entries, replace_sip_uris=False): accounts_cache = {} result = [] for row in entries: + if replace_sip_uris: + row["caller"] = self._process_sip_uri(row.caller) + try: - row.caller_account = accounts_cache[row.caller] + row["caller_account"] = accounts_cache[row.caller] except KeyError: - row.caller_account = accounts_cache[row.caller] = \ + row["caller_account"] = accounts_cache[row.caller] = \ self.accounts.get_by_sip_id(row.caller) + if replace_sip_uris: + row["called"] = self._process_sip_uri(row.called) + try: - row.called_account = accounts_cache[row.called] + row["called_account"] = accounts_cache[row.called] except KeyError: - row.called_account = accounts_cache[row.called] = \ + row["called_account"] = accounts_cache[row.called] = \ self.accounts.get_by_sip_id(row.called) if not row.called_account: - row.called_conference = self.get_conference(row.called) + row["called_conference"] = self.get_conference(row.called) else: - row.called_conference = None + row["called_conference"] = None + + try: + row["time"] = datetime.datetime.fromutctimestamp(row.time) + except: + pass result.append(row) return result def get_call_log(self, account, limit=25): - res = self.db.query("SELECT * FROM cdr \ - WHERE ((direction = 'incoming' AND called = %s) OR (direction = 'outgoing' AND caller = %s)) \ - AND (status = 'answered' OR \ - (status = 'ringing' AND (reason = 'hangup' OR reason = 'Request Terminated'))) \ - AND NOT (status = 'outgoing' AND reason = 'pickup') \ - AND ended = TRUE ORDER BY time DESC LIMIT %s", account.sip_id, account.sip_id, limit) + if account: + res = self.db.query("SELECT * FROM cdr WHERE (caller = %s OR called = %s) \ + ORDER BY time DESC LIMIT %s", account.sip_id, account.sip_id, limit) + else: + res = self.db.query("SELECT * FROM cdr ORDER BY time DESC LIMIT %s", limit) return self._process_cdr(res) @@ -109,18 +141,23 @@ class Talk(Object): if account and sip_id is None: sip_id = account.sip_id + # If the given account does not have a SIP ID, + # we not need to search for any active sessions + if sip_id is None: + return [] + if sip_id: - res = self.db.query("SELECT * FROM cdr \ - WHERE direction = 'incoming' AND (caller = %s OR called = %s) \ - AND status = 'answered' AND NOT ended \ - ORDER BY time", sip_id, sip_id) + sip_id = "sip:%s@%%" % sip_id + + res = self.db.query("SELECT from_uri AS caller, \ + to_uri AS called, start_time AS time FROM dialog \ + WHERE from_uri LIKE %s OR to_uri LIKE %s ORDER BY time", + sip_id, sip_id) else: - res = self.db.query("SELECT * FROM cdr \ - WHERE direction = 'incoming' AND \ - status = 'answered' AND NOT ended \ - ORDER BY time") + res = self.db.query("SELECT from_uri AS caller, to_uri AS called, \ + start_time AS time FROM dialog ORDER BY time") - return self._process_cdr(res) + return self._process_cdr(res, replace_sip_uris=True) def initiate_call(self, caller, called, when=None): self.db.execute("INSERT INTO dialout(caller, called, not_before) \ @@ -129,12 +166,11 @@ class Talk(Object): # Favourites def get_favourite_contacts(self, account, limit=6): - ignored_numbers = ["9999", account.sip_id, ""] - - res = self.db.query("SELECT called, %s AS caller, COUNT(*) AS count FROM cdr \ - WHERE direction = 'outgoing' AND caller = %s AND status = 'answered' \ - AND NOT called = ANY(%s) GROUP BY called ORDER BY count DESC LIMIT %s", - account.sip_id, account.sip_id, ignored_numbers, limit) + res = self.db.query("SELECT src_user AS caller, dst_ouser AS called, \ + COUNT(*) AS count FROM acc WHERE method = %s AND src_user = %s AND \ + dst_ouser BETWEEN %s AND %s AND time >= NOW() - INTERVAL '1 year' \ + GROUP BY caller, called ORDER BY count DESC LIMIT %s", + "INVITE", account.sip_id, "1000", "1999", limit) return self._process_cdr(res) @@ -191,6 +227,9 @@ class Conference(Object): participants = [p.caller for p in self.participants] for invitee in self.backend.talk.get_phonebook(): + if not invitee.sip_id: + continue + if invitee.sip_id in participants: continue diff --git a/webapp/handlers_talk.py b/webapp/handlers_talk.py index e422a0cc..27602e48 100644 --- a/webapp/handlers_talk.py +++ b/webapp/handlers_talk.py @@ -34,29 +34,7 @@ class TalkPhonebookAccountHandler(BaseHandler): if not account: raise tornado.web.HTTPError(404, "Account not found: %s" % uid) - # Get the call log, currently registered lines and ongoing calls - if self.current_user == account or self.current_user.is_admin(): - call_log = self.talk.get_call_log(account) - lines = self.talk.get_lines(account) - ongoing_calls = self.talk.get_ongoing_calls(account) - else: - call_log = None - lines = None - ongoing_calls = None - - self.render("talk/phonebook-contact.html", account=account, - ongoing_calls=ongoing_calls, call_log=call_log, lines=lines) - - -class TalkTrunksHandler(BaseHandler): - @tornado.web.authenticated - def get(self): - if not self.current_user.is_admin(): - raise tornado.web.HTTPError(403) - - trunks = self.talk.get_trunks() - - self.render("talk/trunks.html", trunks=trunks) + self.render("talk/phonebook-contact.html", account=account) class TalkDiagnosisHandler(BaseHandler): diff --git a/webapp/ui_modules.py b/webapp/ui_modules.py index f61cfca8..4b4e9c9c 100644 --- a/webapp/ui_modules.py +++ b/webapp/ui_modules.py @@ -48,6 +48,10 @@ class UIModule(tornado.web.UIModule): def planet(self): return self.handler.planet + @property + def talk(self): + return self.handler.talk + @property def wishlist(self): return self.handler.wishlist @@ -323,13 +327,45 @@ class ProgressBarModule(UIModule): class TalkCallLogModule(UIModule): - def render(self, calls): - return self.render_string("talk/modules/call-log.html", calls=calls) + def render(self, account=None, viewer=None): + if (account is None or not self.current_user == account) \ + and not self.current_user.is_admin(): + raise RuntimeException("Insufficient permissions") + + if viewer is None: + viewer = self.current_user + + calls = self.talk.get_call_log(account) + + return self.render_string("talk/modules/call-log.html", + calls=calls, viewer=viewer) + + +class TalkLinesModule(UIModule): + def render(self, account=None, show_account=False): + if (account is None or not self.current_user == account) \ + and not self.current_user.is_admin(): + raise RuntimeException("Insufficient permissions") + + lines = self.talk.get_lines(account) + + return self.render_string("talk/modules/lines.html", + show_account=show_account, lines=lines) class TalkOngoingCallsModule(UIModule): - def render(self, calls): - return self.render_string("talk/modules/ongoing-calls.html", calls=calls) + def render(self, account=None): + if (account is None or not self.current_user == account) \ + and not self.current_user.is_admin(): + raise RuntimeException("Insufficient permissions") + + calls = self.talk.get_ongoing_calls(account) + + if calls: + return self.render_string("talk/modules/ongoing-calls.html", + calls=calls) + + return "" class TrackerPeerListModule(UIModule): -- 2.39.2