Skip to content

Commit

Permalink
test: InstitutionalUsersReporter
Browse files Browse the repository at this point in the history
  • Loading branch information
aaxelb committed Aug 30, 2024
1 parent 980f5e3 commit 48a6e16
Showing 1 changed file with 221 additions and 0 deletions.
221 changes: 221 additions & 0 deletions osf_tests/metrics/reporters/test_institutional_users_reporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
from __future__ import annotations
import dataclasses
import datetime
import unittest

from django.test import TestCase

from osf import models as osfdb
from osf.metrics.reports import InstitutionalUserReport
from osf.metrics.reporters import InstitutionalUsersReporter
from osf.metrics.utils import YearMonth
from osf_tests.factories import (
InstitutionFactory,
PreprintFactory,
ProjectFactory,
RegistrationFactory,
UserFactory,
EmbargoFactory,
)


def _can_affiliate_preprints() -> bool:
# HACK: preprints affiliation project still in-progress
return hasattr(osfdb.Preprint, 'affiliated_institutions')


def _patch_now(fakenow: datetime.datetime):
return unittest.mock.patch('django.utils.timezone.now', return_value=fakenow)


class TestInstiUsersReporter(TestCase):
@classmethod
def setUpTestData(cls):
cls._yearmonth = YearMonth(2012, 7)
cls._now = datetime.datetime(
cls._yearmonth.year,
cls._yearmonth.month,
13, # just some day in the month
tzinfo=datetime.UTC,
)
with _patch_now(cls._now):
cls._institution = InstitutionFactory()
cls._user_setup_with_nothing = _InstiUserSetup(0, 0, 0, 0, 0, cls._institution, cls._now)
cls._user_setup_with_ones = _InstiUserSetup(1, 1, 1, 1, 1, cls._institution, cls._now)
cls._user_setup_with_stuff = _InstiUserSetup(2, 3, 5, 3, 2, cls._institution, cls._now)
cls._user_setup_with_stuff.fill_uncounted_objects()

def _assert_report_matches_setup(self, report: InstitutionalUserReport, setup: _InstiUserSetup):
self.assertEqual(report.institution_id, setup.institution._id)
# user info:
self.assertEqual(report.user_id, setup.user._id)
self.assertEqual(report.department_name, setup.department_name)
self.assertEqual(report.month_last_login, YearMonth.from_date(setup.user.date_last_login))
self.assertEqual(report.account_creation_date, YearMonth.from_date(setup.user.created))
self.assertEqual(report.orcid_id, setup.orcid_id)
# counts:
self.assertEqual(report.public_project_count, setup.public_project_count)
self.assertEqual(report.private_project_count, setup.private_project_count)
self.assertEqual(report.public_registration_count, setup.public_registration_count)
self.assertEqual(report.embargoed_registration_count, setup.embargoed_registration_count)
# NOTE: currently untested due to the annoyance involved:
# self.assertEqual(report.public_file_count, ...)
# self.assertEqual(report.storage_byte_count, ...)
if _can_affiliate_preprints():
self.assertEqual(report.published_preprint_count, setup.published_preprint_count)
else:
self.assertEqual(report.published_preprint_count, 0)

def test_no_users(self):
_actual_reports = list(InstitutionalUsersReporter().report(self._yearmonth))
self.assertEqual(_actual_reports, [])

def test_one_user_with_nothing(self):
self._user_setup_with_nothing.affiliate_user()
_reports = list(InstitutionalUsersReporter().report(self._yearmonth))
self.assertEqual(len(_reports), 1)
self._assert_report_matches_setup(_reports[0], self._user_setup_with_nothing)

def test_one_user_with_ones(self):
self._user_setup_with_ones.affiliate_user()
_reports = list(InstitutionalUsersReporter().report(self._yearmonth))
self.assertEqual(len(_reports), 1)
self._assert_report_matches_setup(_reports[0], self._user_setup_with_ones)

def test_one_user_with_stuff(self):
self._user_setup_with_stuff.affiliate_user()
_reports = list(InstitutionalUsersReporter().report(self._yearmonth))
self.assertEqual(len(_reports), 1)
self._assert_report_matches_setup(_reports[0], self._user_setup_with_stuff)

def test_several_users(self):
_setups = [
self._user_setup_with_nothing,
self._user_setup_with_ones,
self._user_setup_with_stuff,
]
for _setup in _setups:
_setup.affiliate_user()
_setup_by_userid = {
_setup.user._id: _setup
for _setup in _setups
}
_reports = list(InstitutionalUsersReporter().report(self._yearmonth))
self.assertEqual(len(_reports), len(_setup_by_userid))
for _actual_report in _reports:
_setup = _setup_by_userid[_actual_report.user_id]
self._assert_report_matches_setup(_actual_report, _setup)


# helper class for test-case setup
@dataclasses.dataclass
class _InstiUserSetup:
'''oof, so many things to set up, gross'''
public_project_count: int
private_project_count: int
public_registration_count: int
embargoed_registration_count: int
published_preprint_count: int
institution: osfdb.Institution
now: datetime.datetime
department_name: str | None = None
orcid_id: str | None = None
user: osfdb.OSFUser = dataclasses.field(init=False)

def __post_init__(self):
self.user = UserFactory(
date_last_login=self.now,
external_identity=(
{'ORCID': {self.orcid_id: 'VERIFIED'}}
if self.orcid_id
else {}
),
)
self._add_affiliations(self._generate_counted_objects())

def affiliate_user(self):
self.user.add_or_update_affiliated_institution(
self.institution,
sso_department=self.department_name,
)

@property
def future_timestamp(self):
return self.now + datetime.timedelta(days=123)

def fill_uncounted_objects(self):
# uncounted because not affiliated:
self._add_public_project()
self._add_private_project()
self._add_public_registration()
self._add_embargoed_registration()
self._add_published_preprint()
# uncounted because affiliated with another institution:
self._add_affiliations((
self._add_public_project(),
self._add_private_project(),
self._add_public_registration(),
self._add_embargoed_registration(),
self._add_published_preprint(),
), institution=InstitutionFactory())
# uncounted because created after the report's time range:
with _patch_now(self.future_timestamp):
self._add_affiliations((
self._add_public_project(),
self._add_private_project(),
self._add_public_registration(),
self._add_embargoed_registration(),
self._add_published_preprint(),
))

def _add_affiliations(self, objs, institution=None):
for _obj in objs:
if _obj is not None:
_obj.affiliated_institutions.add(institution or self.institution)

def _generate_counted_objects(self):
for _ in range(self.public_project_count):
yield self._add_public_project()
for _ in range(self.private_project_count):
yield self._add_private_project()
for _ in range(self.public_registration_count):
yield self._add_public_registration()
for _ in range(self.embargoed_registration_count):
yield self._add_embargoed_registration()
for _ in range(self.published_preprint_count):
yield self._add_published_preprint()

def _add_public_project(self) -> osfdb.Node:
return ProjectFactory(
creator=self.user,
is_public=True,
)

def _add_private_project(self) -> osfdb.Node:
return ProjectFactory(
creator=self.user,
is_public=False,
)

def _add_public_registration(self) -> osfdb.Registration:
return RegistrationFactory(
creator=self.user,
is_public=True,
)

def _add_embargoed_registration(self) -> osfdb.Registration:
return RegistrationFactory(
creator=self.user,
is_public=False,
embargo=EmbargoFactory(
user=self.user,
end_date=self.future_timestamp,
),
)

def _add_published_preprint(self) -> osfdb.Preprint | None:
if _can_affiliate_preprints(): # HACK: preprints affiliation project still in-progress
return PreprintFactory(
creator=self.user,
is_public=True,
)

0 comments on commit 48a6e16

Please sign in to comment.