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
18 changes: 8 additions & 10 deletions api/collections/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ def update(self, obj, validated_data):
obj.grade_levels = validated_data.pop('grade_levels')

obj.save()
if waffle.switch_is_active(features.COLLECTION_SUBMISSION_WITH_CEDAR):
obj.sync_cedar_metadata()
return obj


Expand Down Expand Up @@ -405,6 +407,8 @@ def update(self, obj, validated_data):
obj.grade_levels = validated_data.pop('grade_levels')

obj.save()
if waffle.switch_is_active(features.COLLECTION_SUBMISSION_WITH_CEDAR):
obj.sync_cedar_metadata()
return obj


Expand All @@ -429,15 +433,12 @@ def create(self, validated_data):
raise exceptions.ValidationError('"creator" must be specified.')
if not (creator.has_perm('write_collection', collection) or (hasattr(guid.referent, 'has_permission') and guid.referent.has_permission(creator, WRITE))):
raise exceptions.PermissionDenied('Must have write permission on either collection or collected object to collect.')
if waffle.switch_is_active(features.COLLECTION_SUBMISSION_WITH_CEDAR) and collection.provider_id:
try:
collection.provider.validate_required_metadata(guid.referent)
except ValidationError as e:
raise InvalidModelValueError(e.message)
try:
obj = collection.collect_object(guid.referent, creator, **validated_data)
except ValidationError as e:
raise InvalidModelValueError(e.message)
if waffle.switch_is_active(features.COLLECTION_SUBMISSION_WITH_CEDAR):
obj.sync_cedar_metadata()
if subjects:
auth = get_user_auth(self.context['request'])
try:
Expand Down Expand Up @@ -470,15 +471,12 @@ def create(self, validated_data):
raise exceptions.ValidationError('"creator" must be specified.')
if not (creator.has_perm('write_collection', collection) or (hasattr(guid.referent, 'has_permission') and guid.referent.has_permission(creator, WRITE))):
raise exceptions.PermissionDenied('Must have write permission on either collection or collected object to collect.')
if waffle.switch_is_active(features.COLLECTION_SUBMISSION_WITH_CEDAR) and collection.provider_id:
try:
collection.provider.validate_required_metadata(guid.referent)
except ValidationError as e:
raise InvalidModelValueError(e.message)
try:
obj = collection.collect_object(guid.referent, creator, **validated_data)
except ValidationError as e:
raise InvalidModelValueError(e.message)
if waffle.switch_is_active(features.COLLECTION_SUBMISSION_WITH_CEDAR):
obj.sync_cedar_metadata()
if subjects:
auth = get_user_auth(self.context['request'])
try:
Expand Down
79 changes: 70 additions & 9 deletions api_tests/collections/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from api_tests.subjects.mixins import UpdateSubjectsMixin, SubjectsFilterMixin, SubjectsListMixin, \
SubjectsRelationshipMixin
from api_tests.utils import disconnected_from_listeners
from tests.utils import capture_notifications
from framework.auth.core import Auth
from osf import features
from osf.models import Collection, VersionedGuidMixin
Expand Down Expand Up @@ -4450,16 +4451,76 @@ def test_switch_active_no_provider_submission_succeeds(self, app, user_one, proj
)
assert res.status_code == 201

def test_switch_active_missing_cedar_record_submission_fails(self, app, user_one, project, url, payload):
def test_switch_active_no_provider_no_cedar_record_created(self, app, user_one, project, url_no_provider, payload):
from osf.models import CedarMetadataRecord
with mock_update_share():
with override_switch(features.COLLECTION_SUBMISSION_WITH_CEDAR, active=True):
app.post_json_api(url_no_provider, payload(guid=project._id), auth=user_one.auth)
assert not CedarMetadataRecord.objects.filter(guid__in=project.guids.all()).exists()

def test_switch_active_submission_creates_cedar_record(self, app, user_one, project, url, payload, cedar_template):
from osf.models import CedarMetadataRecord
with capture_notifications():
with mock_update_share():
with override_switch(features.COLLECTION_SUBMISSION_WITH_CEDAR, active=True):
res = app.post_json_api(
url,
payload(guid=project._id),
auth=user_one.auth,
)
assert res.status_code == 201
record = CedarMetadataRecord.objects.filter(
guid__in=project.guids.all(),
template=cedar_template,
is_published=True,
).first()
assert record is not None

def test_switch_active_submission_with_custom_fields_syncs_cedar_metadata(
self, app, user_one, project, url, payload, cedar_template, collection):
from osf.models import CedarMetadataRecord
collection.status_choices = ['pending']
collection.volume_choices = ['1']
collection.save()
with capture_notifications():
with mock_update_share():
with override_switch(features.COLLECTION_SUBMISSION_WITH_CEDAR, active=True):
app.post_json_api(
url,
payload(guid=project._id, status='pending', volume='1'),
auth=user_one.auth,
)
record = CedarMetadataRecord.objects.get(guid__in=project.guids.all(), template=cedar_template)
assert record.metadata['status'] == 'pending'
assert record.metadata['volume'] == '1'

def test_switch_inactive_submission_does_not_create_cedar_record(
self, app, user_one, project, url, payload, cedar_template):
from osf.models import CedarMetadataRecord
with capture_notifications():
with mock_update_share():
with override_switch(features.COLLECTION_SUBMISSION_WITH_CEDAR, active=False):
res = app.post_json_api(url, payload(guid=project._id), auth=user_one.auth)
assert res.status_code == 201
assert not CedarMetadataRecord.objects.filter(guid__in=project.guids.all()).exists()

def test_switch_active_update_syncs_cedar_metadata(
self, app, user_one, project, url, payload, cedar_template, collection, provider):
from osf.models import CedarMetadataRecord
collection.status_choices = ['pending', 'approved']
collection.save()
with capture_notifications():
with mock_update_share():
with override_switch(features.COLLECTION_SUBMISSION_WITH_CEDAR, active=True):
res = app.post_json_api(url, payload(guid=project._id, status='pending'), auth=user_one.auth)
assert res.status_code == 201

detail_url = f'/{API_BASE}collections/{collection._id}/collected_metadata/{project._id}/'
with override_switch(features.COLLECTION_SUBMISSION_WITH_CEDAR, active=True):
res = app.post_json_api(
url,
payload(guid=project._id),
auth=user_one.auth,
expect_errors=True,
)
assert res.status_code == 400
assert 'CEDAR metadata record' in res.json['errors'][0]['detail']
app.patch_json_api(detail_url, payload(status='approved'), auth=user_one.auth)

record = CedarMetadataRecord.objects.get(guid__in=project.guids.all(), template=cedar_template)
assert record.metadata['status'] == 'approved'


class TestCollectedMetaSubjectFiltering(SubjectsFilterMixin):
Expand Down
19 changes: 19 additions & 0 deletions osf/models/collection_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@

logger = logging.getLogger(__name__)

CEDAR_METADATA_FIELDS = [
'collected_type', 'status', 'volume', 'issue',
'program_area', 'school_type', 'study_design',
'data_type', 'disease', 'grade_levels',
]


class CollectionSubmission(TaxonomizableMixin, BaseModel):
primary_identifier_name = 'guid___id'
Expand Down Expand Up @@ -94,6 +100,19 @@ def is_submitted_by_moderator_contributor(self, event_data):
def state(self, new_state):
self.machine_state = new_state.value

def sync_cedar_metadata(self):
"""Create or update a CedarMetadataRecord from this submission's custom metadata fields."""

from osf.models import CedarMetadataRecord
if not (self.collection.provider_id and self.collection.provider.required_metadata_template):
return
template = self.collection.provider.required_metadata_template
metadata = {f: getattr(self, f) for f in CEDAR_METADATA_FIELDS if getattr(self, f, '')}
record, _ = CedarMetadataRecord.objects.get_or_create(guid=self.guid, template=template)
record.metadata = metadata
record.is_published = True
record.save()

def _notify_contributors_pending(self, event_data):
user = event_data.kwargs.get('user')
for contributor in self.guid.referent.contributors:
Expand Down
Loading