From 7fe39038898e1ea05baea3a7f3669eb2a699ab92 Mon Sep 17 00:00:00 2001
From: atoav <dh@atoav.com>
Date: Tue, 28 Apr 2020 17:23:10 +0200
Subject: [PATCH] Add a ton of CLI options, formatting fixes & fancy

---
 README.md               |  11 +++-
 bbbmon/bbbmon.py        | 110 +++++++++++++++++++++++++++-------------
 bbbmon/configuration.py |  17 +++++++
 pyproject.toml          |   2 +-
 4 files changed, 103 insertions(+), 37 deletions(-)

diff --git a/README.md b/README.md
index 6cd9aad..5bbe802 100644
--- a/README.md
+++ b/README.md
@@ -57,4 +57,13 @@ bigbluebutton.web.serverURL=https://bbb.example.com/
 securitySalt=MY_SUPER_SECRET_SECRET2
 bigbluebutton.web.serverURL=https://bbb.foo.com/
 ```
-The section names in the square brackets will be used as display names (these support utf-8)
\ No newline at end of file
+The section names in the square brackets will be used as display names (these support utf-8)
+
+# Usage
+
+For help run:
+
+```bash
+bbbmon --help
+```
+
diff --git a/bbbmon/bbbmon.py b/bbbmon/bbbmon.py
index 69ff510..dd24629 100755
--- a/bbbmon/bbbmon.py
+++ b/bbbmon/bbbmon.py
@@ -117,41 +117,67 @@ def format_duration(meeting: XmlDictConfig) -> str:
 
 
 
+def get_formated_presenter_name_id(meeting: XmlDictConfig) -> str:
+    """
+    Get the formated name of the presenter for a given meeting (with id)
+    """
+    presenter = get_presenter(meeting)
+    if presenter is not None:
+        return "{:<30} ({})".format(presenter["fullName"], presenter["userID"])
+    else:
+        return "no Presenter"
+
 def get_formated_presenter_name(meeting: XmlDictConfig) -> str:
     """
     Get the formated name of the presenter for a given meeting
     """
     presenter = get_presenter(meeting)
     if presenter is not None:
-        return "{:<30} ({})".format(presenter["fullName"], presenter["userID"])
+        return "{:<30}".format(presenter["fullName"])
     else:
         return "no Presenter"
 
 
-def print_leaderboard(meetings: Iterable[XmlDictConfig], key: str):
+def print_leaderboard(meetings: Iterable[XmlDictConfig], key: str, endpoint_name: str, presenter: bool, presenter_id: bool, fancy: bool):
     """
     Print a leaderboard of all meetings sorted by a given key (e.g. 
     participantCount)
     """
-    print("LEADERBOARD ({})".format(FRIENDLY_KEYNAMES[key]))
+    print_header(endpoint_name, "LEADERBOARD ({})".format(FRIENDLY_KEYNAMES[key]), fancy)
     sorted_by = sorted([m for m in meetings], key=lambda x:int(x[key]), reverse=True)
     for m in sorted_by:
-        print("{:>5} {:<45} {}".format(m[key], m["meetingName"], get_formated_presenter_name(m))) 
-
-
-def print_duration_leaderboard(meetings: Iterable[XmlDictConfig]):
+        if presenter:
+            if presenter_id:
+                print("{:>5} {:<45} {}".format(m[key], m["meetingName"], get_formated_presenter_name_id(m))) 
+            else:
+                print("{:>5} {:<45} {}".format(m[key], m["meetingName"], get_formated_presenter_name(m))) 
+        else:
+            print("{:>5} {}".format(m[key], m["meetingName"]))
+
+def print_duration_leaderboard(meetings: Iterable[XmlDictConfig], endpoint_name: str, presenter: bool, presenter_id: bool, fancy: bool):
     """
     Print a leaderboard of all meetings sorted by a given key (e.g. 
     participantCount)
     """
-    print("LEADERBOARD (Duration)")
+    print_header(endpoint_name, "LEADERBOARD (Duration)", fancy)
     by_duration = sorted([m for m in meetings], key=lambda x:int(get_duration(x).total_seconds()), reverse=True)
 
     for m in by_duration:
-        print("{:>12} {:<45} {}".format(format_duration(m), m["meetingName"], get_formated_presenter_name(m))) 
-
+        if presenter:
+            if presenter_id:
+                print("{:>12} {:<38} {}".format(format_duration(m), m["meetingName"], get_formated_presenter_name_id(m))) 
+            else:
+                print("{:>12} {:<38} {}".format(format_duration(m), m["meetingName"], get_formated_presenter_name(m))) 
+        else:
+            print("{:>12} {}".format(format_duration(m), m["meetingName"]))
+
+def print_header(endpoint_name: str, text: str, fancy=True):
+    if fancy:
+        click.echo(click.style("  [{}] {}  ".format(endpoint_name, text), fg='black', bg='white', bold=True))
+    else:
+        print("[{}] {}".format(endpoint_name, text))
 
-def print_overview(config: Config):
+def print_overview(config: Config, leaderboards: bool, participants: bool, presenter: bool, presenter_id: bool, show_meetings: bool, fancy: bool):
     """
     For each endpoint in the configuration get the active meetings and print 
     out an overview of the current bbb-usage
@@ -162,12 +188,14 @@ def print_overview(config: Config):
         # Print divider if there is more than one endpoint
         if i > 0:
             print()
-            print("="*80)
+            print("="*click.get_terminal_size()[0])
             print()
 
         # If there are no meetings, skip to next endpoint
         if len(meetings) == 0:
-            print("MEETINGS on [{}]: None".format(endpoint.name))
+            if show_meetings:
+                print_header(endpoint.name, "MEETINGS", fancy)
+                print("   └─── Currently no active meetings.")
             continue
 
         n_running = len(meetings)
@@ -178,25 +206,29 @@ def print_overview(config: Config):
         n_video = sum([int(m["videoCount"]) for m in meetings])
         n_moderator = sum([int(m["moderatorCount"]) for m in meetings])
 
-        print("MEETINGS on [{}]:".format(endpoint.name))
-        print("   ├─── {:>4} running".format(n_running))
-        print("   └─── {:>4} recording".format(n_recording))
-        print()
-        print("PARTICIPANTS across all {} rooms".format(n_running))
-        print("   └─┬─ {:>4} total".format(n_participants))
-        print("     ├─ {:>4} listening only".format(n_listeners))
-        print("     ├─ {:>4} mic on".format(n_voice))
-        print("     ├─ {:>4} video on".format(n_video))
-        print("     └─ {:>4} moderators".format(n_moderator))
+        if show_meetings:
+            print_header(endpoint.name, "MEETINGS", fancy)
+            print("   ├─── {:>4} running".format(n_running))
+            print("   └─── {:>4} recording".format(n_recording))
+            print()
 
-        print()
-        print_leaderboard(meetings, "participantCount")
-        print()
-        print_leaderboard(meetings, "videoCount")
-        print()
-        print_leaderboard(meetings, "voiceParticipantCount")
-        print()
-        print_duration_leaderboard(meetings)
+        if participants:
+            print_header(endpoint.name, "PARTICIPANTS across all {} rooms".format(n_running), fancy)
+            print("   └─┬─ {:>4} total".format(n_participants))
+            print("     ├─ {:>4} listening only".format(n_listeners))
+            print("     ├─ {:>4} mic on".format(n_voice))
+            print("     ├─ {:>4} video on".format(n_video))
+            print("     └─ {:>4} moderators".format(n_moderator))
+
+        if leaderboards:
+            print()
+            print_leaderboard(meetings, "participantCount", endpoint.name, presenter, presenter_id, fancy)
+            print()
+            print_leaderboard(meetings, "videoCount", endpoint.name, presenter, presenter_id, fancy)
+            print()
+            print_leaderboard(meetings, "voiceParticipantCount", endpoint.name, presenter, presenter_id, fancy)
+            print()
+            print_duration_leaderboard(meetings, endpoint.name, presenter, presenter_id, fancy)
 
 
 def init_config() -> Optional[Config]:
@@ -227,10 +259,18 @@ def init_config() -> Optional[Config]:
         exit()
 
 
-
-def main():
-    Config = init_config()
-    print_overview(Config)
+@click.command()
+@click.option('--endpoint', '-e', multiple=True, help="Filter by one or more endpoints as named in the user configuration. Order is respected.")
+@click.option('--leaderboards/--no-leaderboards', default=True, show_default=True, help="Hide or show the meeting leaderboards")
+@click.option('--participants/--no-participants', default=True, show_default=True, help="Hide or show the participants")
+@click.option('--meetings/--no-meetings', default=True, show_default=True, help="Hide or show the meetings")
+@click.option('--presenter/--no-presenter', default=True, show_default=True, help="Hide or show the presenters")
+@click.option('--presenter-id/--no-presenter-id', default=True, show_default=True, help="Hide or show the presenter IDs")
+@click.option('--fancy/--no-fancy', default=True, show_default=True, help="Use fancy headers")
+def main(leaderboards, participants, presenter, presenter_id, meetings, endpoint, fancy):
+    config = init_config()
+    config.filter_endpoints(endpoint)
+    print_overview(config, leaderboards, participants, presenter, presenter_id, meetings, fancy)
 
 
 if __name__ == "__main__":
diff --git a/bbbmon/configuration.py b/bbbmon/configuration.py
index 0b0d725..dade7de 100644
--- a/bbbmon/configuration.py
+++ b/bbbmon/configuration.py
@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 import configparser
 from typing import NewType, Optional, Tuple, Iterable, List
+import click
 
 # Default path
 SERVER_PROPERTIES_FILE = "/usr/share/bbb-web/WEB-INF/classes/bigbluebutton.properties"
@@ -55,6 +56,22 @@ class Config():
             self = self.from_server(path)
             return self
 
+    def filter_endpoints(self, endpoint_options: List[str]) -> 'Config':
+        if len(endpoint_options) == 0:
+            return self
+        else:
+            existing_names = [e.name for e in self.endpoints]
+            filtered_endpoints = []
+            for endpoint_option in endpoint_options:
+                if not endpoint_option in existing_names:
+                    click.echo("{} there is no endpoint called \"{}\" in the configuration. It will be ignored.\n".format(click.style('Error:', fg='red', bold=True), click.style(endpoint_option, fg='red', bold=True)))
+                else:
+                    filtered_endpoints.append([e for e in self.endpoints if e.name == endpoint_option][0])
+
+            self.endpoints = filtered_endpoints
+            return self
+
+
     def __len__(self):
         """
         The length of a Config is represented by the number of endpoints
diff --git a/pyproject.toml b/pyproject.toml
index 1e9b799..c275727 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "bbbmon"
-version = "0.1.8"
+version = "0.1.9"
 description = "A small CLI utility to monitor bbb usage"
 authors = ["David Huss <david.huss@hfbk-hamburg.de>"]
 maintainers = ["David Huss <david.huss@hfbk-hamburg.de>"]
-- 
GitLab