diff --git a/osf/metadata/osf_gathering.py b/osf/metadata/osf_gathering.py index 9d97782dfff..46084d51a17 100644 --- a/osf/metadata/osf_gathering.py +++ b/osf/metadata/osf_gathering.py @@ -27,6 +27,7 @@ without_namespace, smells_like_iri, ) +from osf.metrics.reports import PublicItemUsageReport from osf.utils import workflows as osfworkflows from osf.utils.outcomes import ArtifactTypes from website import settings as website_settings @@ -220,16 +221,22 @@ def osfmap_supplement_for_type(rdftype_iri: str): OSFMAP_SUPPLEMENT = { OSF.Project: { + OSF.usage: None, }, OSF.ProjectComponent: { + OSF.usage: None, }, OSF.Registration: { + OSF.usage: None, }, OSF.RegistrationComponent: { + OSF.usage: None, }, OSF.Preprint: { + OSF.usage: None, }, OSF.File: { + OSF.usage: None, }, } @@ -1051,3 +1058,29 @@ def gather_cedar_templates(focus): template_iri = rdflib.URIRef(record.get_template_semantic_iri()) yield (OSF.hasCedarTemplate, template_iri) yield (template_iri, DCTERMS.title, record.get_template_name()) + + +@gather.er(OSF.usage) +def gather_last_month_usage(focus): + _search = ( + PublicItemUsageReport.search() + .filter('term', item_osfid=osfguid_from_iri(focus.iri)) + # only last month's report + .filter('range', report_yearmonth={'gte': 'now-1M/M'}) + .sort('-report_yearmonth') + [:1] + ) + _reports = list(_search.execute()) + if _reports: + _usage_report = _reports[0] + _usage_report_ref = rdflib.BNode() + yield (OSF.usage, _usage_report_ref) + yield (_usage_report_ref, DCAT.accessService, rdflib.URIRef(website_settings.DOMAIN.rstrip('/'))) + yield (_usage_report_ref, DCTERMS.temporal, rdflib.Literal( + str(_usage_report.report_yearmonth), + datatype=rdflib.XSD.gYearMonth, + )) + yield (_usage_report_ref, OSF.viewCount, _usage_report.view_count) + yield (_usage_report_ref, OSF.viewSessionCount, _usage_report.view_session_count) + yield (_usage_report_ref, OSF.downloadCount, _usage_report.download_count) + yield (_usage_report_ref, OSF.downloadSessionCount, _usage_report.download_session_count) diff --git a/osf/metrics/counted_usage.py b/osf/metrics/counted_usage.py index 393bd0558c0..c3c6d4cc1aa 100644 --- a/osf/metrics/counted_usage.py +++ b/osf/metrics/counted_usage.py @@ -10,7 +10,6 @@ import pytz from osf.metrics.utils import stable_key -from osf.models import Guid logger = logging.getLogger(__name__) @@ -87,6 +86,7 @@ def _autofill_fields(sender, instance, **kwargs): _fill_pageview_info(instance) item_guid = getattr(instance, 'item_guid', None) if item_guid: + from osf.models import Guid guid_instance = Guid.load(item_guid) if guid_instance and guid_instance.referent: _fill_osfguid_info(instance, guid_instance.referent) diff --git a/osf/metrics/reports.py b/osf/metrics/reports.py index 3a5f4b1b449..605830d3eae 100644 --- a/osf/metrics/reports.py +++ b/osf/metrics/reports.py @@ -4,9 +4,13 @@ from django.dispatch import receiver from elasticsearch6_dsl import InnerDoc from elasticsearch_metrics import metrics -from elasticsearch_metrics.signals import pre_save as metrics_pre_save +from elasticsearch_metrics.signals import ( + pre_save as metrics_pre_save, + post_save as metrics_post_save, +) from osf.metrics.utils import stable_key, YearMonth +from website import settings as website_settings class ReportInvalid(Exception): @@ -289,3 +293,14 @@ class PublicItemUsageReport(MonthlyReport): # download counts of this item only (not including contained components or files) download_count = metrics.Long() # counter:Total_Item_Requests download_session_count = metrics.Long() # counter:Unique_Item_Requests + + +@receiver(metrics_post_save, sender=PublicItemUsageReport) +def update_supplementary_metadata(sender, instance, **kwargs): + if website_settings.SHARE_ENABLED: + from api.share.utils import task__update_share + task__update_share.apply_async( + args=(instance.item_osfid,), + kwargs={'is_supplementary': True, 'is_backfill': True}, + countdown=30, # delay 30 seconds; plenty of time for index refresh + ) diff --git a/osf/models/node.py b/osf/models/node.py index 9e342308f44..62925966e2e 100644 --- a/osf/models/node.py +++ b/osf/models/node.py @@ -80,7 +80,6 @@ from api.caching.tasks import update_storage_usage from api.caching import settings as cache_settings from api.caching.utils import storage_usage_cache -from api.share.utils import update_share logger = logging.getLogger(__name__) @@ -711,6 +710,7 @@ def should_request_identifiers(self): @classmethod def bulk_update_search(cls, nodes, index=None): + from api.share.utils import update_share for _node in nodes: update_share(_node) from website import search @@ -722,6 +722,7 @@ def bulk_update_search(cls, nodes, index=None): log_exception(e) def update_search(self): + from api.share.utils import update_share update_share(self) from website import search try: diff --git a/osf/models/user.py b/osf/models/user.py index 29e10efa991..438c8d3938e 100644 --- a/osf/models/user.py +++ b/osf/models/user.py @@ -34,7 +34,6 @@ MergeConflictError) from framework.exceptions import PermissionsError from framework.sessions.utils import remove_sessions_for_user -from api.share.utils import update_share from osf.utils.requests import get_current_request from osf.exceptions import reraise_django_validation_errors, UserStateError from .base import BaseModel, GuidMixin, GuidMixinQuerySet @@ -1451,6 +1450,7 @@ def is_assumed_ham(self): return user_has_trusted_email def update_search(self): + from api.share.utils import update_share update_share(self) from website.search.search import update_user update_user(self) diff --git a/osf_tests/metadata/test_osf_gathering.py b/osf_tests/metadata/test_osf_gathering.py index 7bd72770aba..4d6fd418ebe 100644 --- a/osf_tests/metadata/test_osf_gathering.py +++ b/osf_tests/metadata/test_osf_gathering.py @@ -11,6 +11,7 @@ FOAF, OSF, OSFIO, + DCAT, DCTERMS, DCMITYPE, DOI, @@ -20,11 +21,14 @@ checksum_iri, ) from osf import models as osfdb +from osf.metrics.reports import PublicItemUsageReport +from osf.metrics.utils import YearMonth from osf.utils import permissions, workflows from osf_tests import factories from website import settings as website_settings from website.project import new_bookmark_collection from osf_tests.metadata._utils import assert_triples +from osf_tests.metrics.utils_for_tests import es_metrics_temps class TestOsfGathering(TestCase): @@ -750,3 +754,28 @@ def test_gather_cedar_templates(self): (self.filefocus.iri, OSF.hasCedarTemplate, cedar_template_iri), (cedar_template_iri, DCTERMS.title, Literal(self.cedar_template.schema_name)) }) + + @es_metrics_temps() + def test_gather_last_month_usage(self): + # no usage report: + assert_triples(osf_gathering.gather_last_month_usage(self.projectfocus), set()) + # yes usage report: + _ym = YearMonth.from_date(datetime.datetime.now(tz=datetime.UTC)) + PublicItemUsageReport( + item_osfid=self.project._id, + report_yearmonth=_ym, + view_count=71, + view_session_count=13, + download_count=43, + download_session_count=11, + ).save(refresh=True) + _usage_bnode = rdflib.BNode() + assert_triples(osf_gathering.gather_last_month_usage(self.projectfocus), { + (self.projectfocus.iri, OSF.usage, _usage_bnode), + (_usage_bnode, DCTERMS.temporal, Literal(str(_ym), datatype=rdflib.XSD.gYearMonth)), + (_usage_bnode, DCAT.accessService, rdflib.URIRef(website_settings.DOMAIN.rstrip('/'))), + (_usage_bnode, OSF.viewCount, Literal(71)), + (_usage_bnode, OSF.viewSessionCount, Literal(13)), + (_usage_bnode, OSF.downloadCount, Literal(43)), + (_usage_bnode, OSF.downloadSessionCount, Literal(11)), + })