Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cds_migrator_kit/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,9 @@ class RecordFlaggedCuration(CDSMigrationException):
"""Record statistics error."""

description = "[Record needs to be curated]"


class MissingConfiguration(CDSMigrationException):
"""Missing configuration exception."""

description = "[Missing configuration]"
5 changes: 5 additions & 0 deletions cds_migrator_kit/rdm/migration_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,11 @@ def resolve_record_pid(pid):
"""Absolute path to the vocabularies directory. Defaults to
{instance_path}/app_data/vocabularies when None."""

CDS_CERN_SCIENTIFIC_COMMUNITY_ID = os.environ.get(
"CDS_CERN_SCIENTIFIC_COMMUNITY_ID", None
)
"""Community slug or UUID for CERN Research."""

CDS_ACCESS_GROUP_MAPPINGS = {
"SSO": ["cern-accounts-primary"],
"ITDepRestrFile": ["it-dep"],
Expand Down
16 changes: 16 additions & 0 deletions cds_migrator_kit/rdm/records/transform/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,19 @@


FILE_SUBFORMATS_TO_DROP = ["pdfa", "unstamped"]

# Public research publication resource types that are auto-included in the CERN Research community.
CDS_CERN_SCIENTIFIC_RESOURCE_TYPES = {
"publication-dissertation", # Already included by the migrator for thesis records.
"publication-book",
"publication-section",
"publication-conferencepaper",
"publication-conferenceproceeding",
"publication-journal",
"publication-article",
"publication-preprint",
"publication-report",
"publication-technicalnote",
"publication-note",
"publication",
}
30 changes: 28 additions & 2 deletions cds_migrator_kit/rdm/records/transform/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# the terms of the MIT License; see LICENSE file for more details.

"""CDS-RDM transform step module."""
import datetime

import logging
from collections import OrderedDict
from copy import deepcopy
Expand All @@ -32,11 +32,12 @@

from cds_migrator_kit.errors import (
ManualImportRequired,
MissingConfiguration,
MissingRequiredField,
MultipleModelsMatched,
RecordFlaggedCuration,
RestrictedFileDetected,
UnexpectedValue,
MultipleModelsMatched,
)
from cds_migrator_kit.rdm.migration_config import (
RDM_RECORDS_IDENTIFIERS_SCHEMES,
Expand All @@ -48,6 +49,7 @@
IDENTIFIERS_VALUES_TO_DROP,
PIDS_SCHEMES_ALLOWED,
PIDS_SCHEMES_TO_DROP,
CDS_CERN_SCIENTIFIC_RESOURCE_TYPES,
)
from cds_migrator_kit.transform.dumper import CDSRecordDump
from cds_migrator_kit.transform.errors import LossyConversion
Expand Down Expand Up @@ -793,9 +795,32 @@ def __init__(
self.db_state = {"affiliations": CDSMigrationAffiliationMapping}
super().__init__(workers, throw)

def _should_add_scientific_community(self, record):
if self.restricted or record.get("access") != "public":
return False
resource_type_id = (
record.get("json", {})
.get("metadata", {})
.get("resource_type", {})
.get("id")
)
return resource_type_id in CDS_CERN_SCIENTIFIC_RESOURCE_TYPES

def _communities_ids(self, entry, record):
communities = record.get("communities", [])
communities = self.communities_ids + [slug for slug in communities]

scientific_community = current_app.config.get(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a failsafe like in the other PRs to check if the variable is set? does it fail silently now? maybe it makes sense to add this case to tests

"CDS_CERN_SCIENTIFIC_COMMUNITY_ID"
)
if not scientific_community:
raise MissingConfiguration(
"CDS_CERN_SCIENTIFIC_COMMUNITY_ID is not configured"
)
if self._should_add_scientific_community(record):
if scientific_community not in communities:
communities.append(scientific_community)

if communities:
return {"ids": communities, "default": self.communities_ids[0]}
return {}
Expand Down Expand Up @@ -849,6 +874,7 @@ def _transform(self, entry):
ManualImportRequired,
MissingRequiredField,
MultipleModelsMatched,
MissingConfiguration,
) as e:
migration_logger.add_log(e, record=entry)

Expand Down
12 changes: 12 additions & 0 deletions tests/cds-rdm/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,18 @@ def community(running_app, db):
return comm


@pytest.fixture()
def cern_scientific_community(running_app, db, app):
"""A CERN Research community fixture."""
comm = Community.create({})
comm.slug = "cern-research"
comm.metadata = {"title": "CERN Research"}
comm.commit()
db.session.commit()
app.config["CDS_CERN_SCIENTIFIC_COMMUNITY_ID"] = str(comm.id)
return comm


# @pytest.fixture()
# def users(app, db):
# """Create example user."""
Expand Down
9 changes: 9 additions & 0 deletions tests/cds-rdm/test_full_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ def test_full_migration_stream(
superuser_identity,
orcid_name_data,
community,
cern_scientific_community, # Fixture for the CERN Scientific community, so that it gets auto included for PUBLIC-ations.
mocker,
groups,
):
Expand Down Expand Up @@ -494,6 +495,8 @@ def test_full_migration_stream(
if record["legacy_recid"] == "2294138":
author_with_inspire(loaded_rec)

# Check if the record is also included in the CERN Scientific community since it is a publication report
scientific_community_inclusion(loaded_rec, cern_scientific_community.id)
# Check if remote account has the correct metadata
# Check if user profile has the correct metadata
user_metadata()
Expand Down Expand Up @@ -563,3 +566,9 @@ def user_metadata():
"person_id": "11115",
"department": "IT",
}


def scientific_community_inclusion(record, cern_scientific_community_uuid):
"""Checks if the record is included in the CERN Scientific community."""
assert str(cern_scientific_community_uuid) in record._record.parent.communities.ids
assert cern_scientific_community_uuid != record._record.parent.communities.default
1 change: 1 addition & 0 deletions tests/cds-rdm/test_hr_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# the terms of the MIT License; see LICENSE file for more details.

"""Tests suites."""

import json
from pathlib import Path

Expand Down Expand Up @@ -67,7 +68,7 @@
runner.run()

# 2 Test records
assert CDSMigrationLegacyRecord.query.count() == 2

Check failure on line 71 in tests/cds-rdm/test_hr_migration.py

View workflow job for this annotation

GitHub Actions / RDMTests (3.9, pypi, postgresql14)

test_full_hr_stream assert 0 == 2 + where 0 = count() + where count = <flask_sqlalchemy.query.Query object at 0x7f425fcc0cd0>.count + where <flask_sqlalchemy.query.Query object at 0x7f425fcc0cd0> = CDSMigrationLegacyRecord.query

legacy_recid = "2647384"
RDMRecord.index.refresh()
Expand Down
145 changes: 145 additions & 0 deletions tests/cds-rdm/test_scientific_community.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2026 CERN.
#
# CDS-RDM is free software; you can redistribute it and/or modify it under
# the terms of the MIT License; see LICENSE file for more details.

"""Tests for auto-inclusion in the CERN Research community."""

from unittest.mock import MagicMock

import pytest

from cds_migrator_kit.errors import MissingConfiguration
from cds_migrator_kit.rdm.records.transform.config import CDS_CERN_SCIENTIFIC_RESOURCE_TYPES
from cds_migrator_kit.rdm.records.transform.transform import CDSToRDMRecordTransform


def _test_record(
access="public",
resource_type="publication-preprint",
communities=[],
recid="123456",
):
"""Build a minimal CDSToRDMRecordEntry.transform() output for community tests."""
metadata = {
"title": "Test record",
"publication_date": "2020-01-01",
}
if resource_type is not None:
metadata["resource_type"] = {"id": resource_type}

return {
"recid": recid,
"access": access,
"communities": communities,
"json": {
"files": {"enabled": False},
"metadata": metadata,
},
}


@pytest.fixture
def transform(tmp_path, community):
"""Transform instance with a collection community configured."""
return CDSToRDMRecordTransform(
files_dump_dir=tmp_path,
missing_users=tmp_path,
communities_ids=[str(community.id)],
migration_logger=MagicMock(),
)


class TestCommunitiesIds:
"""Test CDSToRDMRecordTransform._communities_ids()."""

def test_adds_scientific_community_for_public_research_test_record(
self, transform, community, cern_scientific_community
):
"""Public research records are included in the CERN Scientific community."""
result = transform._communities_ids({}, _test_record())

assert result == {
"ids": [str(community.id), str(cern_scientific_community.id)],
"default": str(community.id),
}

def test_keep_collection_community_as_default(
self, transform, community, cern_scientific_community
):
"""Collection community remains the default when CERN Scientific community is added."""
result = transform._communities_ids(
{},
_test_record(communities=["test-community"]),
)

assert result["default"] == str(community.id)
assert result["ids"] == [
str(community.id),
"test-community",
str(cern_scientific_community.id),
]

@pytest.mark.parametrize("resource_type", CDS_CERN_SCIENTIFIC_RESOURCE_TYPES)
def test_research_resource_types(
self, transform, cern_scientific_community, resource_type
):
"""All configured public research resource types trigger inclusion."""
result = transform._communities_ids(
{}, _test_record(resource_type=resource_type)
)

assert str(cern_scientific_community.id) in result["ids"]
assert cern_scientific_community.id != result["default"]

def test_skip_restricted_test_record(
self, transform, community, cern_scientific_community
):
"""Restricted records are not included in the CERN Research community."""
result = transform._communities_ids({}, _test_record(access="restricted"))

assert result == {
"ids": [str(community.id)],
"default": str(community.id),
}

def test_skip_non_research_resource_type(
self, transform, community, cern_scientific_community
):
"""Non-research resource types are not included in the CERN Scientific community."""
result = transform._communities_ids({}, _test_record(resource_type="other"))

assert result == {
"ids": [str(community.id)],
"default": str(community.id),
}

def test_skip_when_stream_is_restricted(
self, tmp_path, community, cern_scientific_community
):
"""Records on restricted migration streams are not included in the CERN Scientific community."""
transform = CDSToRDMRecordTransform(
files_dump_dir=tmp_path,
missing_users=tmp_path,
communities_ids=[str(community.id)],
restricted=True,
migration_logger=MagicMock(),
)

result = transform._communities_ids({}, _test_record())

assert result == {
"ids": [str(community.id)],
"default": str(community.id),
}

def test_raise_when_cern_scientific_community_not_configured(
self, test_app, transform, community
):
"""No CERN Scientific community is added when config is unset."""
test_app.config["CDS_CERN_SCIENTIFIC_COMMUNITY_ID"] = None

with pytest.raises(MissingConfiguration):
transform._communities_ids({}, _test_record())
Loading