diff --git a/db.py b/db.py index f4ec4dd..0906fa8 100644 --- a/db.py +++ b/db.py @@ -19,9 +19,16 @@ NOTIFICATION_MESSAGES = collections.deque([], NOTIFICATION_BUFFER_SIZE) NOTIFICATION_HISTORY_BUFFER_RESOLUTION_PER_MIN = 12 NOTIFICATION_HISTORY_BUFFER_TIMESPAN_MIN = 20 NOTIFICATION_HISTORY_FREQUENCY = 60 / NOTIFICATION_HISTORY_BUFFER_RESOLUTION_PER_MIN -notification_history_size = NOTIFICATION_HISTORY_BUFFER_RESOLUTION_PER_MIN * NOTIFICATION_HISTORY_BUFFER_TIMESPAN_MIN -NOTIFICATION_HISTORY = collections.deque([], notification_history_size) -NOTIFICATION_HISTORY.extend([0] * notification_history_size) +notification_history_buffer_size = NOTIFICATION_HISTORY_BUFFER_RESOLUTION_PER_MIN * NOTIFICATION_HISTORY_BUFFER_TIMESPAN_MIN +NOTIFICATION_HISTORY = collections.deque([], notification_history_buffer_size) +NOTIFICATION_HISTORY.extend([0] * notification_history_buffer_size) + +USER_ACTIVITY_BUFFER_RESOLUTION_PER_MIN = 2 +USER_ACTIVITY_TIMESPAN_MIN = 20 +USER_ACTIVITY_FREQUENCY = 60 / USER_ACTIVITY_BUFFER_RESOLUTION_PER_MIN +USER_ACTIVITY = {} +user_activity_buffer_size = USER_ACTIVITY_BUFFER_RESOLUTION_PER_MIN * USER_ACTIVITY_TIMESPAN_MIN + def resetNotificationMessage(): global NOTIFICATION_MESSAGES @@ -29,5 +36,18 @@ def resetNotificationMessage(): def resetNotificationHistory(): global NOTIFICATION_HISTORY - NOTIFICATION_HISTORY = collections.deque([], notification_history_size) - NOTIFICATION_HISTORY.extend([0] * notification_history_size) \ No newline at end of file + NOTIFICATION_HISTORY = collections.deque([], notification_history_buffer_size) + NOTIFICATION_HISTORY.extend([0] * notification_history_buffer_size) + +def addUserActivity(user_id: int, count: int): + global USER_ACTIVITY, USER_ACTIVITY_TIMESPAN_MIN + + if user_id not in USER_ACTIVITY: + USER_ACTIVITY[user_id] = collections.deque([], user_activity_buffer_size) + USER_ACTIVITY[user_id].extend([0] * user_activity_buffer_size) + USER_ACTIVITY[user_id].append(count) + +def resetUserActivity(): + for user_id in USER_ACTIVITY.keys(): + USER_ACTIVITY[user_id] = collections.deque([], user_activity_buffer_size) + USER_ACTIVITY[user_id].extend([0] * user_activity_buffer_size) \ No newline at end of file diff --git a/notification.py b/notification.py index d08f87d..4c6be5d 100644 --- a/notification.py +++ b/notification.py @@ -27,14 +27,26 @@ def get_notifications() -> list[dict]: return list(db.NOTIFICATION_MESSAGES) -def get_notifications_history() -> list[dict]: +def get_notifications_history() -> dict: return { 'history': list(db.NOTIFICATION_HISTORY), 'config': { 'buffer_resolution_per_minute': db.NOTIFICATION_HISTORY_BUFFER_RESOLUTION_PER_MIN, 'buffer_timestamp_min': db.NOTIFICATION_HISTORY_BUFFER_TIMESPAN_MIN, 'frequency': db.NOTIFICATION_HISTORY_FREQUENCY, - 'notification_history_size': db.notification_history_size, + 'notification_history_size': db.notification_history_buffer_size, + }, + } + + +def get_users_activity() -> dict: + return { + 'activity': {user_id: list(activity) for user_id, activity in db.USER_ACTIVITY.items()}, + 'config': { + 'timestamp_min': db.USER_ACTIVITY_TIMESPAN_MIN, + 'buffer_resolution_per_minute': db.USER_ACTIVITY_BUFFER_RESOLUTION_PER_MIN, + 'frequency': db.USER_ACTIVITY_FREQUENCY, + 'activity_buffer_size': db.user_activity_buffer_size, }, } @@ -51,6 +63,10 @@ def record_notification_history(message_count: int): db.NOTIFICATION_HISTORY.append(message_count) +def record_user_activity(user_id: int, count: int): + db.addUserActivity(user_id, count) + + def get_user_id(data: dict): if 'user_id' in data: return int(data['user_id']) diff --git a/server.py b/server.py index d2137d6..9d879d5 100755 --- a/server.py +++ b/server.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import collections import functools import json import sys @@ -20,6 +21,7 @@ import misp_api ZMQ_MESSAGE_COUNT_LAST_TIMESPAN = 0 ZMQ_MESSAGE_COUNT = 0 ZMQ_LAST_TIME = None +USER_ACTIVITY = collections.defaultdict(int) def debounce(debounce_seconds: int = 1): @@ -109,6 +111,10 @@ async def reset_notifications(sid): async def get_diagnostic(sid): return await getDiagnostic() +@sio.event +async def get_users_activity(sid): + return notification_model.get_users_activity() + @sio.event async def toggle_verbose_mode(sid, payload): return notification_model.set_verbose_mode(payload['verbose']) @@ -144,6 +150,9 @@ async def handleMessage(topic, s, message): if notification_model.is_accepted_notification(notification): notification_model.record_notification(notification) ZMQ_MESSAGE_COUNT_LAST_TIMESPAN += 1 + user_id = notification_model.get_user_id(data) + if user_id is not None: + USER_ACTIVITY[user_id] += 1 await sio.emit('notification', notification) user_id = notification_model.get_user_id(data) @@ -201,6 +210,18 @@ async def notification_history(): await sio.emit('update_notification_history', payload) +async def record_users_activity(): + global USER_ACTIVITY + + while True: + await sio.sleep(db.USER_ACTIVITY_FREQUENCY) + for user_id, activity in USER_ACTIVITY.items(): + notification_model.record_user_activity(user_id, activity) + USER_ACTIVITY[user_id] = 0 + payload = notification_model.get_users_activity() + await sio.emit('update_users_activity', payload) + + async def keepalive(): global ZMQ_LAST_TIME while True: @@ -237,6 +258,7 @@ async def init_app(): sio.start_background_task(forward_zmq_to_socketio) sio.start_background_task(keepalive) sio.start_background_task(notification_history) + sio.start_background_task(record_users_activity) sio.start_background_task(backup_exercises_progress) return app diff --git a/src/components/LiveLogsUserActivityGraph.vue b/src/components/LiveLogsUserActivityGraph.vue new file mode 100644 index 0000000..82873da --- /dev/null +++ b/src/components/LiveLogsUserActivityGraph.vue @@ -0,0 +1,129 @@ + + + \ No newline at end of file diff --git a/src/components/TheLiveLogsActivityGraph.vue b/src/components/TheLiveLogsActivityGraph.vue index 64c0d6b..9868b40 100644 --- a/src/components/TheLiveLogsActivityGraph.vue +++ b/src/components/TheLiveLogsActivityGraph.vue @@ -5,7 +5,6 @@ const theChart = ref(null) const chartInitSeries = [ - // {data: Array.apply(null, {length: 240}).map(Function.call, Math.random)} {data: Array.from(Array(12*20)).map(()=> 0)} ] const hasActivity = computed(() => notificationHistory.value.length > 0) diff --git a/src/components/TheScores.vue b/src/components/TheScores.vue index 23b2acd..de270da 100644 --- a/src/components/TheScores.vue +++ b/src/components/TheScores.vue @@ -3,6 +3,7 @@ import { active_exercises as exercises, progresses, setCompletedState } from "@/socket"; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { faCheck, faTimes, faGraduationCap, faMedal, faHourglassHalf } from '@fortawesome/free-solid-svg-icons' + import LiveLogsUserActivityGraph from "./LiveLogsUserActivityGraph.vue" const collapsed_panels = ref([]) @@ -85,11 +86,14 @@