mirror of
https://codeberg.org/langfingaz/bbb-status
synced 2024-12-23 00:36:05 +01:00
work in progress: print fullNames of recent moderators
Use-case: Get an overview of active users that create and start lots of meetings. Inform them that the instance is more and more used and ask for a small donation.
This commit is contained in:
parent
154f2ed333
commit
1e5b44f2c9
@ -4,6 +4,7 @@ import logging
|
||||
|
||||
from langfingaz import plotMeetings
|
||||
from langfingaz import logMeetingData
|
||||
from langfingaz import recentModerators
|
||||
|
||||
|
||||
def main():
|
||||
@ -17,7 +18,8 @@ def main():
|
||||
'\tlog: log BBB meeting data every 5 minutes;\n' + \
|
||||
'\t prints one dot after each successfully saved file\n' + \
|
||||
'\tlog-verbose: log BBB meeting every 5 minute and write current status to stdout\n' + \
|
||||
'\tplot: saves a plot of the saved BBB meeting data\n'
|
||||
'\tplot: saves a plot of the saved BBB meeting data\n' + \
|
||||
'\tmoderator: prints users that have recently been moderators in meetings\n'
|
||||
|
||||
if len(argv) != 2:
|
||||
raise ValueError("Expected one commandline argument!\n" + usageStr)
|
||||
@ -28,6 +30,8 @@ def main():
|
||||
logMeetingData.v2()
|
||||
elif argv[1] == "plot":
|
||||
plotMeetings.plotMeetings()
|
||||
elif argv[1] == "moderator":
|
||||
recentModerators.printModerators()
|
||||
else:
|
||||
print(usageStr)
|
||||
exit(1)
|
||||
|
@ -1,8 +1,13 @@
|
||||
from functools import total_ordering
|
||||
from typing import List
|
||||
from pathlib import Path
|
||||
from xml.etree import ElementTree
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
from langfingaz import loadData
|
||||
from langfingaz.util import util
|
||||
from langfingaz.util import fileUtil
|
||||
|
||||
|
||||
class Meeting(object):
|
||||
@ -39,8 +44,11 @@ class Meeting(object):
|
||||
|
||||
class Attendee(object):
|
||||
def __init__(self, xml_attendee: ElementTree.Element):
|
||||
# self.userID = xml_attendee.find('userID').text
|
||||
# self.fullName = xml_attendee.find('fullName').text
|
||||
userID = xml_attendee.find('userID').text
|
||||
fullName = xml_attendee.find('fullName').text
|
||||
self.user = User(userID=userID, fullName=fullName)
|
||||
|
||||
# roles: MODERATOR, VIEWER
|
||||
self.role = xml_attendee.find('role').text
|
||||
self.isPresenter: bool = util.asBoolean(xml_attendee.find('isPresenter').text)
|
||||
self.isListeningOnly: bool = util.asBoolean(xml_attendee.find('isListeningOnly').text)
|
||||
@ -51,6 +59,34 @@ class Attendee(object):
|
||||
return util.asString(self)
|
||||
|
||||
|
||||
@total_ordering
|
||||
class User(object):
|
||||
def __init__(self, userID: str, fullName: str):
|
||||
self.userID = userID
|
||||
self.fullName = fullName
|
||||
|
||||
@staticmethod
|
||||
def _is_valid_operand(other):
|
||||
return (hasattr(other, "userID") and
|
||||
hasattr(other, "fullName"))
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.userID)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not self._is_valid_operand(other):
|
||||
return NotImplemented
|
||||
return self.userID == other.userID
|
||||
|
||||
def __lt__(self, other):
|
||||
if not self._is_valid_operand(other):
|
||||
return NotImplemented
|
||||
return self.userID < other.userID
|
||||
|
||||
def __str__(self):
|
||||
return self.userID + " - " + self.fullName
|
||||
|
||||
|
||||
class BbbStatus(object):
|
||||
"""
|
||||
Represents the status of one BBB server at one point of time.
|
||||
@ -104,3 +140,24 @@ def parseMeetingsData(dataStr: str) -> List[Meeting]:
|
||||
xml_meetings = tree.find('meetings')
|
||||
|
||||
return [Meeting(xml_meeting) for xml_meeting in xml_meetings.iter('meeting')]
|
||||
|
||||
|
||||
def readBbbStatiFromDir(dataDir: Path = fileUtil.getDataDir()) -> List[BbbStatus]:
|
||||
"""
|
||||
:return: List of BbbStatus objects, sorted by date
|
||||
"""
|
||||
|
||||
bbbStati: List[BbbStatus] = []
|
||||
|
||||
for file in dataDir.iterdir():
|
||||
if file.name.endswith(".xml"):
|
||||
logging.debug("Reading from file " + str(file))
|
||||
dataStr, t = loadData.loadData(file)
|
||||
meetings: List[Meeting] = parseMeetingsData(dataStr)
|
||||
bbbStati.append(BbbStatus(meetings, t))
|
||||
|
||||
if (len(bbbStati) < 1):
|
||||
print("No bbbStatus objects were found in data directory: " + str(dataDir))
|
||||
|
||||
# sort by date
|
||||
return sorted(bbbStati, key=BbbStatus.getKey)
|
||||
|
@ -22,21 +22,10 @@ def plotMeetings(dataDir: Path = fileUtil.getDataDir()):
|
||||
plot BBB meetings of the last month
|
||||
"""
|
||||
|
||||
bbbStati: List[BbbStatus] = []
|
||||
|
||||
for file in dataDir.iterdir():
|
||||
if file.name.endswith(".xml"):
|
||||
logging.debug("Reading from file " + str(file))
|
||||
dataStr, t = loadData.loadData(file)
|
||||
meetings: List[Meeting] = parseMeetings.parseMeetingsData(dataStr)
|
||||
bbbStati.append(parseMeetings.BbbStatus(meetings, t))
|
||||
if (len(bbbStati) < 1):
|
||||
print("No bbbStatus objects could be read from data directory: " + str(dataDir))
|
||||
bbbStati: List[BbbStatus] = parseMeetings.readBbbStatiFromDir(dataDir)
|
||||
if len(bbbStati) < 1:
|
||||
return
|
||||
|
||||
# sort by date (x-axis)
|
||||
bbbStati = sorted(bbbStati, key=BbbStatus.getKey)
|
||||
|
||||
# filter: take only bbbStatus objects of the last month (4 weeks)
|
||||
#
|
||||
# start: datetime = bbbStati[0].pointOfTime
|
||||
|
54
src/langfingaz/recentModerators.py
Normal file
54
src/langfingaz/recentModerators.py
Normal file
@ -0,0 +1,54 @@
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import List, Dict
|
||||
import logging
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
|
||||
from langfingaz import loadData
|
||||
from langfingaz import parseMeetings
|
||||
from langfingaz.parseMeetings import BbbStatus, Meeting
|
||||
from langfingaz.util import fileUtil
|
||||
from langfingaz.util import util
|
||||
|
||||
|
||||
def getModerators(dataDir: Path = fileUtil.getDataDir()) -> Dict[parseMeetings.User, int]:
|
||||
"""
|
||||
Logged in users have a consistent userId and fullName.
|
||||
But if they join a meeting via a link they might be asked for a name and get a new userId.
|
||||
|
||||
This method goes through all BbbStati and filters for moderators.
|
||||
It then counts how often a moderator was found in one BbbStati.
|
||||
|
||||
If a BbbStati are saved every 5 minutes, a user might have a count of 4 if he was moderator
|
||||
at just one meeting for >20 minutes
|
||||
|
||||
:return: Dict[User, count-how-often-as-moderator] sorted by User.fullName
|
||||
"""
|
||||
|
||||
bbbStati: List[BbbStatus] = parseMeetings.readBbbStatiFromDir(dataDir)
|
||||
if len(bbbStati) < 1:
|
||||
return {}
|
||||
|
||||
moderators: List[parseMeetings.User] = [attendee.user
|
||||
for bbbStatus in bbbStati
|
||||
for meeting in bbbStatus.meetings
|
||||
for attendee in meeting.attendees
|
||||
if attendee.role == "MODERATOR"]
|
||||
|
||||
moderatorCount: Dict[parseMeetings.User, int] = {}
|
||||
for moderator in moderators:
|
||||
count: int = 1
|
||||
if moderator in moderatorCount:
|
||||
count = moderatorCount.get(moderator) + 1
|
||||
moderatorCount[moderator] = count
|
||||
|
||||
# sort by User.fullName
|
||||
moderatorCount = dict(sorted(moderatorCount.items(), key=lambda item: item[0].fullName))
|
||||
return moderatorCount
|
||||
|
||||
|
||||
def printModerators():
|
||||
moderators: Dict[parseMeetings.User, int] = getModerators()
|
||||
[print(moderator.fullName + ":\t" + str(count)) for moderator, count in moderators.items()]
|
Loading…
Reference in New Issue
Block a user