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