-
Notifications
You must be signed in to change notification settings - Fork 330
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
221 additions
and
0 deletions.
There are no files selected for viewing
221 changes: 221 additions & 0 deletions
221
osf_tests/metrics/reporters/test_institutional_users_reporter.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
) |