plot data

This commit is contained in:
Daniel Langbein 2020-11-28 21:11:21 +01:00
parent 35b305393a
commit 32d8158921
15 changed files with 214 additions and 71 deletions

View File

@ -6,7 +6,7 @@ COPY requirements.txt ./
RUN pip3 install --no-cache-dir -r requirements.txt RUN pip3 install --no-cache-dir -r requirements.txt
COPY ./src/ ./ COPY ./src/ ./
RUN chmod +x ./logMeetingData.py RUN chmod +x ./langfingaz/logMeetingData.py
# unbuffered output option otherwise script sleeps before any output appears # unbuffered output option otherwise script sleeps before any output appears
CMD [ "python", "-u", "./logMeetingData.py" ] CMD [ "python", "-u", "./langfingaz/logMeetingData.py" ]

View File

@ -2,8 +2,9 @@ version: '3.7'
services: services:
dk-gen: dk-gen:
build: . build: .
# environment: environment:
# - PYTHONPATH=/usr/src/ # pythonpath is required to import from self created modules (langfingaz)
- PYTHONPATH=/usr/src/
volumes: volumes:
- ./secret:/usr/secret - ./secret:/usr/secret
- ./data:/usr/data - ./data:/usr/data

0
plot/dummy Normal file
View File

View File

View File

@ -2,7 +2,7 @@ import hashlib
import requests import requests
from xml.etree import ElementTree from xml.etree import ElementTree
import util import langfingaz.util.fileUtil as fileUtil
def requestMeetingData() -> str: def requestMeetingData() -> str:
@ -31,16 +31,16 @@ def getRequestUrl(api_method: str = 'getMeetings', query_string: str = '') -> st
def getUrl() -> str: def getUrl() -> str:
filepath = util.getWorkingDir().joinpath("../secret").joinpath("url.txt") filepath = fileUtil.getProjectBaseDir().joinpath("secret").joinpath("url.txt")
url = util.readFirstLine(filepath).strip() url = fileUtil.readFirstLine(filepath).strip()
if not url.endswith("/"): if not url.endswith("/"):
raise ValueError("url should end with '/'") raise ValueError("url should end with '/'")
return url return url
def getSecret() -> str: def getSecret() -> str:
filepath = util.getWorkingDir().joinpath("../secret").joinpath("secret.txt") filepath = fileUtil.getProjectBaseDir().joinpath("secret").joinpath("secret.txt")
secret = util.readFirstLine(filepath).strip() secret = fileUtil.readFirstLine(filepath).strip()
min_length = 12 min_length = 12
if len(secret) <= min_length: if len(secret) <= min_length:
raise ValueError("secret should be longer than {} characters!".format(min_length)) raise ValueError("secret should be longer than {} characters!".format(min_length))

View File

@ -0,0 +1,12 @@
from typing import Tuple
from datetime import datetime
from pathlib import Path
from langfingaz.util import fileUtil
def loadData(file: Path) -> Tuple[str, datetime]:
dataStr: str = file.read_text()
t: datetime = fileUtil.getDatetimePrefix(file)
return dataStr, t

View File

@ -1,11 +1,10 @@
#!/usr/bin/python3 #!/usr/bin/python3
import time import time
from pathlib import Path
import saveMeetings from langfingaz import parseMeetings, bbbRequest, saveData
import bbbRequest from langfingaz.util import util as util
import parseMeetings
import util
def sleepFiveMin(verbose=False): def sleepFiveMin(verbose=False):
@ -17,13 +16,12 @@ def sleepFiveMin(verbose=False):
time.sleep(fiveMinutes) time.sleep(fiveMinutes)
def v2(): def v2(folder: Path = saveData.getDefaultFolder()):
print("BBB meetingData logger started!") print("BBB meetingData logger started!")
while True: while True:
meetingsStr = bbbRequest.requestMeetingData() meetingsStr = bbbRequest.requestMeetingData()
saveDir = util.getWorkingDir().joinpath("../data") savedFile = saveData.saveMeetingsData(meetingsStr, folder)
savedFile = saveMeetings.saveMeetingsData(meetingsStr, saveDir)
print("Saved meetings at {}".format(savedFile)) print("Saved meetings at {}".format(savedFile))
meetings = parseMeetings.parseMeetingsData(meetingsStr) meetings = parseMeetings.parseMeetingsData(meetingsStr)
@ -34,10 +32,9 @@ def v2():
sleepFiveMin(verbose=True) sleepFiveMin(verbose=True)
def v1(): def v1(folder: Path = saveData.getDefaultFolder()):
while True: while True:
saveDir = util.getWorkingDir().joinpath("../data") saveData.requestAndSaveMeetingData(folder)
saveMeetings.requestAndSaveMeetingData(saveDir)
print('.', end='') print('.', end='')
sleepFiveMin() sleepFiveMin()

View File

@ -1,7 +1,8 @@
from typing import List from typing import List
from xml.etree import ElementTree from xml.etree import ElementTree
from datetime import datetime
import util from langfingaz.util import util
class Meeting(object): class Meeting(object):
@ -11,7 +12,8 @@ class Meeting(object):
self.internalMeetingID: str = xml_meeting.find('internalMeetingID').text self.internalMeetingID: str = xml_meeting.find('internalMeetingID').text
self.isRunning: bool = util.asBoolean(xml_meeting.find('running').text) self.isRunning: bool = util.asBoolean(xml_meeting.find('running').text)
self.startTime = xml_meeting.find('startTime').text self.startTime: int = int(xml_meeting.find('startTime').text)
self.createTime: int = int(xml_meeting.find('createTime').text)
if self.isRunning: if self.isRunning:
self.endTime = None self.endTime = None
else: else:
@ -56,11 +58,14 @@ class BbbStatus(object):
and (TODO) it's CPU utilization and network usage. and (TODO) it's CPU utilization and network usage.
""" """
def __init__(self, meetings: List[Meeting]): def __init__(self, meetings: List[Meeting], pointOfTime: datetime = None):
""" """
:param meetings: All current meetings :param meetings: All current meetings
:param pointOfTime: The date and time at which the information about
the running meetings has been captured / requested from the BBB instance
""" """
self.meetings = meetings self.meetings = meetings
self.pointOfTime = pointOfTime
self.recordingCount = 0 self.recordingCount = 0
self.participantCount = 0 self.participantCount = 0

View File

@ -0,0 +1,52 @@
from pathlib import Path
from typing import List
import matplotlib.pyplot as plt # TODO
from datetime import datetime
from langfingaz import loadData
from langfingaz import parseMeetings
from langfingaz.parseMeetings import BbbStatus, Meeting
from langfingaz.util import fileUtil
def plotMeetings(folder: Path):
bbbStati: List[BbbStatus] = []
for file in folder.iterdir():
if file.name.endswith(".xml"):
dataStr, t = loadData.loadData(file)
meetings: List[Meeting] = parseMeetings.parseMeetingsData(dataStr)
bbbStati.append(parseMeetings.BbbStatus(meetings, t))
doPlotMeetings(bbbStati)
def doPlotMeetings(bbbStati: List[BbbStatus]):
time = [] # x-axis: time
participants = [] # yAxis (1)
videos = [] # yAxis (2)
voices = [] # yAxis (3)
for bbbStatus in bbbStati:
time.append(bbbStatus.pointOfTime)
participants.append(bbbStatus.participantCount)
videos.append(bbbStatus.videoCount)
voices.append(bbbStatus.voiceParticipantCount)
# Note that even in the OO-style, we use `.pyplot.figure` to create the figure.
fig, ax = plt.subplots() # Create a figure and an axes.
ax.plot(time, participants, label='participants') # Plot some data on the axes.
ax.plot(time, videos, label='video') # Plot more data on the axes...
ax.plot(time, voices, label='voice') # ... and some more.
ax.set_xlabel('time') # Add an x-label to the axes.
ax.set_ylabel('numbers') # Add a y-label to the axes.
ax.set_title("BigBlueButton Statistics") # Add a title to the axes.
ax.legend() # Add a legend.
fig.savefig(fileUtil.setDatetimePrefix(fileUtil.getProjectBaseDir().joinpath("plot"), datetime.now()))
plt.show()
if __name__ == '__main__':
plotMeetings(fileUtil.getProjectBaseDir().joinpath("data"))

View File

@ -1,10 +1,16 @@
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
import bbbRequest import langfingaz.util.fileUtil as fileUtil
# import util.util as util
from langfingaz import bbbRequest
def requestAndSaveMeetingData(folder: Path) -> Path: def getDefaultFolder() -> Path:
return fileUtil.getProjectBaseDir().joinpath("data")
def requestAndSaveMeetingData(folder: Path = getDefaultFolder()) -> Path:
""" """
save a new xml file in the given folder save a new xml file in the given folder
@ -16,7 +22,7 @@ def requestAndSaveMeetingData(folder: Path) -> Path:
return saveMeetingsData(bbbRequest.requestMeetingData(), folder) return saveMeetingsData(bbbRequest.requestMeetingData(), folder)
def saveMeetingsData(dataStr: str, folder: Path) -> Path: def saveMeetingsData(dataStr: str, folder: Path = getDefaultFolder()) -> Path:
""" """
save a new xml file in the given folder save a new xml file in the given folder
@ -28,7 +34,7 @@ def saveMeetingsData(dataStr: str, folder: Path) -> Path:
return doSaveData(dataStr, folder, 'meetings') return doSaveData(dataStr, folder, 'meetings')
def doSaveData(dataStr: str, folder: Path, dataType: str = 'meetings') -> Path: def doSaveData(dataStr: str, folder: Path, dataType: str) -> Path:
""" """
save a new xml file in the given folder save a new xml file in the given folder
@ -38,11 +44,9 @@ def doSaveData(dataStr: str, folder: Path, dataType: str = 'meetings') -> Path:
:return: Path to created file :return: Path to created file
""" """
# time_str = datetime.utcnow().strftime('%Y%m%d_%H%M%S') fileWithoutDate = folder.joinpath(dataType + '.xml')
time_str = datetime.now().strftime('%Y%m%d_%H%M%S') prefixedFile = fileUtil.setDatetimePrefix(fileWithoutDate, datetime.now())
filename = '{}_{}.xml'.format(time_str, dataType)
filepath = folder.joinpath(filename)
with open(filepath, "w") as xml_file: with open(prefixedFile, "w") as xml_file:
xml_file.write(dataStr) xml_file.write(dataStr)
return filepath return prefixedFile

View File

View File

@ -0,0 +1,51 @@
from datetime import datetime
from pathlib import Path
import os
def getWorkingDir() -> Path:
return Path(os.path.dirname(os.path.realpath(__file__)))
def getProjectBaseDir() -> Path:
return getWorkingDir().parent.parent.parent
def readFirstLine(file: Path) -> str:
"""
:param file: Path to file
:return: first line of file
"""
with open(file, "r") as f:
return f.readline()
def __getDatetimePrefixLength() -> int:
return len("20200101_120030") # 1st January 2020, 12:00 and 30 seconds
def setDatetimePrefix(file: Path, t: datetime) -> Path:
# filename = file.name
prefix = t.strftime('%Y%m%d_%H%M%S')
filename = prefix + "_" + file.name
return file.parent.joinpath(filename)
def removeDatetimePrefix(file: Path) -> Path:
prefixLen = __getDatetimePrefixLength()
# prefixlen + 1 to remove the underline!
return file.parent.joinpath(file.name[prefixLen + 1:])
def getDatetimePrefix(file: Path) -> datetime:
"""
:param file: some file which filename is prefixed with a date in the form %Y%m%d_%H%M%S
:return: date from filename
"""
prefixLen = __getDatetimePrefixLength()
if len(file.name) < prefixLen:
raise ValueError("Given file does not seem to contain a datetime prefix!")
prefix = file.name[:prefixLen]
return datetime.strptime(prefix, '%Y%m%d_%H%M%S')

View File

@ -1,18 +1,3 @@
from pathlib import Path
import os
def getWorkingDir() -> Path:
return Path(os.path.dirname(os.path.realpath(__file__)))
def readFirstLine(file: Path) -> str:
"""
:param file: Path to file
:return: first line of file
"""
with open(file, "r") as f:
return f.readline()
def indentMultilineStr(s: str, indentWith='\t'): def indentMultilineStr(s: str, indentWith='\t'):
indented = indentWith + s.replace('\n', '\n' + indentWith) indented = indentWith + s.replace('\n', '\n' + indentWith)
if s.endswith('\n'): if s.endswith('\n'):

View File

@ -0,0 +1,60 @@
import unittest
from langfingaz.util import util as util
import langfingaz.util.fileUtil as fileUtil
from pathlib import Path
from datetime import datetime
class UtilTestCase(unittest.TestCase):
def test_indentString(self):
unindented = "Hello\nWorld!"
expected = "\tHello\n\tWorld!"
actual = util.indentMultilineStr(unindented)
self.assertEqual(expected, actual)
def test_indentString2(self):
unindented = "Hello\nWorld!\n"
expected = "\tHello\n\tWorld!\n"
actual = util.indentMultilineStr(unindented)
self.assertEqual(expected, actual)
def test_addPrefixDate(self):
file: Path = Path("/foo/bar")
t: datetime = datetime.strptime('20200101_120030', '%Y%m%d_%H%M%S')
expectedFile: Path = Path("/foo/20200101_120030_bar")
# ACT
prefixedFile: Path = fileUtil.setDatetimePrefix(file, t)
# ASSERT
self.assertEqual(type(expectedFile), type(prefixedFile))
self.assertEqual(expectedFile, prefixedFile)
def test_readPrefixDate(self):
file: Path = Path("/foo/20200101_120030_bar")
expectedDate = datetime.replace(year=2020, month=1, day=1, hour=12, minute=0, second=1)
# ACT
dateFromFile: datetime = fileUtil.getDatetimePrefix(file)
# ASSERT
self.assertEqual(type(expectedDate), type(dateFromFile))
self.assertEqual(expectedDate, dateFromFile)
def test_setAndRemoveDatetimePrefix(self):
t: datetime = datetime.strptime('20200101_120030', '%Y%m%d_%H%M%S')
file: Path = Path("/foo/bar")
prefixed: Path = fileUtil.setDatetimePrefix(file, t)
actual: Path = fileUtil.removeDatetimePrefix(prefixed)
self.assertEqual(type(file), type(actual))
self.assertEqual(file, actual)
if __name__ == '__main__':
unittest.main()

View File

@ -1,24 +0,0 @@
import unittest
import util
class UtilTestCase(unittest.TestCase):
def test_indentString(self):
unindented = "Hello\nWorld!"
expected = "\tHello\n\tWorld!"
actual = util.indentMultilineStr(unindented)
self.assertEqual(expected, actual)
def test_indentString2(self):
unindented = "Hello\nWorld!\n"
expected = "\tHello\n\tWorld!\n"
actual = util.indentMultilineStr(unindented)
self.assertEqual(expected, actual)
if __name__ == '__main__':
unittest.main()