Skip to content
Snippets Groups Projects
Commit ca68c1a9 authored by E. Madison Bray's avatar E. Madison Bray
Browse files

[enhancement] implement new 'stats' RPC method for recsystems

currently this is not used by the backend, and is only used for testing, so in
principle it is not required to be implemented by all recsystems

however, it may be useful for debugging and testing
parent f906df0f
No related branches found
No related tags found
No related merge requests found
......@@ -66,6 +66,8 @@ class BaselineRecsystem(RenewalRecsystem):
async with session.get(url_for('user_assignments')) as resp:
self.users = set(await resp.json())
self.log.debug(f'cached {len(self.users)} assigned users')
# Set the assigned_users stat to the self.users set
self.runtime_stats['assigned_users'] = self.users
########## RPC methods ##########
# WARNING: Don't forget to make these functions async even if they
......@@ -73,6 +75,8 @@ class BaselineRecsystem(RenewalRecsystem):
async def new_article(self, article):
"""Called when a new article was made available from the backend."""
await super().new_article(article)
self.articles.push(article)
self.log.debug(f'new article added to the collection: {article}')
self.log.debug(f'article collection size: {len(self.articles)}')
......
import abc
import copy
import logging
import sys
from urllib.parse import splittype, urljoin
......@@ -39,6 +40,7 @@ class RenewalRecsystem(JSONRPCServerWebsocketClient, metaclass=abc.ABCMeta):
'new_article',
'recommend',
'ping',
'stats',
'unassigned_user'
]
"""List of method names that implement RPC methods."""
......@@ -49,6 +51,16 @@ class RenewalRecsystem(JSONRPCServerWebsocketClient, metaclass=abc.ABCMeta):
EVENT_STREAM_ENDPOINT = '/event_stream'
"""This is the endpoint on the Renewal API to connect to the websocket."""
runtime_stats = {
'ping_count': 0,
'seen_articles': set(),
'assigned_users': set()
}
"""
Runtime statistics of the recsystem; see the `RenewalRecsystem.stats`
RPC method.
"""
def __init__(self, api_base_uri=None, token=None, path=None, log=None):
if api_base_uri is not None:
self.api_base_uri = api_base_uri
......@@ -67,6 +79,8 @@ class RenewalRecsystem(JSONRPCServerWebsocketClient, metaclass=abc.ABCMeta):
self.EVENT_STREAM_ENDPOINT.lstrip('/'))
self.token = token
# Copy the default stats from the class attribute
self.runtime_stats = copy.deepcopy(self.runtime_stats)
super().__init__(websocket_uri, path=path, log=log)
@property
......@@ -105,12 +119,43 @@ class RenewalRecsystem(JSONRPCServerWebsocketClient, metaclass=abc.ABCMeta):
Must just return with the value ``'pong'``.
"""
self.runtime_stats['ping_count'] += 1
return 'pong'
async def stats(self):
"""
Return various statistics about the recsystem since starting up.
* ping_count: number of times the recsystem has responded to a ping
* seen_articles: article_ids of new articles seen by the recsystem
(via the `RenewalRecsystem.new_article` RPC call, not counting
articles manually downloaded via the REST API)
* assigned_users: user_ids of currently assigned users
The default implementation simply returns ``self.runtime_stats``;
subclasses may add additional statistics if they wish, or return a
stub, as this is currently only used for testing the recsystem.
"""
stats = copy.copy(self.runtime_stats)
# The default stats contain a number of sets but these aren't
# JSON-serializable by default; we could use a custom JSON serializer
# but jsonrpcserver doesn't make it all that easy to do that, so
# just perform the necessary conversions here.
for k, v in list(stats.items()):
if isinstance(v, set):
stats[k] = list(v)
return stats
@abc.abstractmethod
async def new_article(self, article):
"""Called when a new article was made available from the backend."""
# Update the seen_articles stat
self.runtime_stats['seen_articles'].add(article['article_id'])
@abc.abstractmethod
async def article_interaction(self, interaction):
"""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment