diff --git a/bravo_api/blueprints/status/status.py b/bravo_api/blueprints/status/status.py index 9bb8f8e..2fea93a 100644 --- a/bravo_api/blueprints/status/status.py +++ b/bravo_api/blueprints/status/status.py @@ -72,7 +72,8 @@ def usage_stats(db: pymongo.database.Database) -> dict: """ result = {"active": active_user_count(db.auth_log), "new": new_user_count(db.users), - "total": total_user_count(db.users)} + "total": total_user_count(db.users), + "max_user_per_day": max_users_per_day(db.auth_log)} return result @@ -141,3 +142,40 @@ def active_user_count(collection: pymongo.collection.Collection) -> dict: cursor = collection.aggregate(pipeline) return([item for item in cursor]) + + +def max_users_per_day(collection: pymongo.collection.Collection) -> dict: + """ + Given pymongo collection for the auth log, query for count of max users in a day per month. + Return array of dicts one per month. E.g. + + [{'month': 10, 'max_daily_users': 10, 'year': 2023}, + {'month': 9, 'max_daily_users': 20, 'year': 2023}, + {'month': 8, 'max_daily_users': 25, 'year': 2023}] + """ + pipeline = [ + {"$project": { + "year": {"$year": "$timestamp"}, + "month": {"$month": "$timestamp"}, + "day": {"$dayOfMonth": "$timestamp"}, + "user_id": "$user_id"}}, + {"$group": {"_id": {"day": "$day", "month": "$month", "year": "$year"}, + "users": {"$addToSet": "$user_id"}}}, + {"$project": { + "_id": 0, + "year": "$_id.year", + "month": "$_id.month", + "day": "$_id.day", + "active_users": {"$size": "$users"}}}, + {"$group": {"_id": {"month": "$month", "year": "$year"}, + "max_user_per_day": {"$max": "$active_users"}}}, + {"$project": { + "_id": 0, + "year": "$_id.year", + "month": "$_id.month", + "max_user_per_day": 1}}, + {"$sort": {"year": -1, "month": -1}} + ] + + cursor = collection.aggregate(pipeline) + return([item for item in cursor]) diff --git a/tests/mongo_fixtures/auth_log.json b/tests/mongo_fixtures/auth_log.json index 1b80c46..89b5d44 100644 --- a/tests/mongo_fixtures/auth_log.json +++ b/tests/mongo_fixtures/auth_log.json @@ -46,5 +46,45 @@ { "_id": { "$oid": "65258e0e61cb53bdab0bec7f" }, "user_id": "carol@example.com", "timestamp": { "$date": 1696945614857 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000000" }, + "user_id": "dan@example.com", + "timestamp": { "$date": 1698797400000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000001" }, + "user_id": "ellis@example.com", + "timestamp": { "$date": 1698798000000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000002" }, + "user_id": "fred@example.com", + "timestamp": { "$date": 1698798600000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000003" }, + "user_id": "gigi@example.com", + "timestamp": { "$date": 1698799200000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000004" }, + "user_id": "han@example.com", + "timestamp": { "$date": 1698799800000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000005" }, + "user_id": "ivan@example.com", + "timestamp": { "$date": 1698800400000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000006" }, + "user_id": "joan@example.com", + "timestamp": { "$date": 1698883800000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000007" }, + "user_id": "kelly@example.com", + "timestamp": { "$date": 1698884400000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000008" }, + "user_id": "liam@example.com", + "timestamp": { "$date": 1698885000000 } + }, + { "_id": { "$oid": "c0ffeec0ffeec0ffee000009" }, + "user_id": "noah@example.com", + "timestamp": { "$date": 1698885600000 } } ] diff --git a/tests/status/test_usage_status.py b/tests/status/test_usage_status.py index 7864dd8..977bdaf 100644 --- a/tests/status/test_usage_status.py +++ b/tests/status/test_usage_status.py @@ -4,15 +4,23 @@ app = Flask('dummy') app.register_blueprint(status.bp) -# Expected results from the mongodb fixtures in tests/mongo_fixtures/ -ACTIVE_USERS_EXPECTED = [{'month': 10, 'active_users': 1, 'year': 2023}, +# Expected results of mongo querying functions when using +# the mongodb fixtures in tests/mongo_fixtures/ +ACTIVE_USERS_EXPECTED = [{'month': 11, 'active_users': 10, 'year': 2023}, + {'month': 10, 'active_users': 1, 'year': 2023}, {'month': 9, 'active_users': 2, 'year': 2023}, {'month': 8, 'active_users': 3, 'year': 2023}] +MAX_DAILY_EXPECTED = [{'max_user_per_day': 6, 'month': 11, 'year': 2023}, + {'max_user_per_day': 1, 'month': 10, 'year': 2023}, + {'max_user_per_day': 1, 'month': 9, 'year': 2023}, + {'max_user_per_day': 1, 'month': 8, 'year': 2023}] + NEW_USERS_EXPECTED = [{'month': 10, 'new_users': 1, 'year': 2023}, {'month': 9, 'new_users': 2, 'year': 2023}, {'month': 8, 'new_users': 1, 'year': 2023}] + TOTAL_USERS_EXPECTED = 4 @@ -46,8 +54,6 @@ def test_returns_version_when_defined(): def test_active_user_query(mongodb): result = status.active_user_count(mongodb.auth_log) - - assert len(result) == 3 assert(result == ACTIVE_USERS_EXPECTED) @@ -66,10 +72,10 @@ def test_total_user_query(mongodb): def test_usage_statistic(mongodb): expected = {"active": ACTIVE_USERS_EXPECTED, "new": NEW_USERS_EXPECTED, - "total": TOTAL_USERS_EXPECTED} + "total": TOTAL_USERS_EXPECTED, + "max_user_per_day": MAX_DAILY_EXPECTED} result = status.usage_stats(mongodb) - assert(len(result.keys()) == 3) assert(result == expected) @@ -86,9 +92,15 @@ def test_usage_endpoint(mocker, mongodb): expected = {"active": ACTIVE_USERS_EXPECTED, "new": NEW_USERS_EXPECTED, - "total": TOTAL_USERS_EXPECTED} + "total": TOTAL_USERS_EXPECTED, + "max_user_per_day": MAX_DAILY_EXPECTED} with app.test_client() as client: resp = client.get('/usage') content = resp.get_json() assert(content == expected) + + +def test_max_users_per_day(mocker, mongodb): + result = status.max_users_per_day(mongodb.auth_log) + assert(result == MAX_DAILY_EXPECTED)