Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
from contentcuration.viewsets.sync.constants import CONTENTNODE_PREREQUISITE
from contentcuration.viewsets.sync.constants import UPDATED


nested_subjects = [subject for subject in SUBJECTSLIST if "." in subject]


Expand Down Expand Up @@ -816,6 +815,47 @@ def test_update_contentnode_update_options_completion_criteria_threshold_only(se
self.assertEqual(c.extra_fields["options"]["completion_criteria"]["model"], completion_criteria.TIME)
self.assertEqual(c.extra_fields["options"]["completion_criteria"]["threshold"], 10)

def test_update_completion_criteria_model_to_determined_by_resource_edge_case(self):
metadata = self.contentnode_db_metadata
metadata["kind_id"] = content_kinds.HTML5
metadata["extra_fields"] = {
"options": {
"completion_criteria": {
"model": completion_criteria.REFERENCE,
"threshold": None,
"learner_managed": False
}
}
}
contentnode = models.ContentNode.objects.create(**metadata)

response = self.sync_changes(
[
generate_update_event(
contentnode.id,
CONTENTNODE,
{
"complete": True,
"extra_fields.options.completion_criteria.threshold": 600,
"extra_fields.options.completion_criteria.model": completion_criteria.APPROX_TIME
},
channel_id=self.channel.id
),
generate_update_event(
contentnode.id,
CONTENTNODE,
{
"extra_fields.options.completion_criteria.model": completion_criteria.DETERMINED_BY_RESOURCE
},
channel_id=self.channel.id
)
],
)
self.assertEqual(len(response.data["errors"]), 0)
updated_contentnode = models.ContentNode.objects.get(id=contentnode.id)
self.assertEqual(updated_contentnode.extra_fields["options"]["completion_criteria"]["model"], completion_criteria.DETERMINED_BY_RESOURCE)
self.assertNotIn("threshold", updated_contentnode.extra_fields["options"]["completion_criteria"])

def test_update_contentnode_update_options_invalid_completion_criteria(self):
metadata = self.contentnode_db_metadata
metadata["extra_fields"] = {
Expand Down
8 changes: 8 additions & 0 deletions contentcuration/contentcuration/utils/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,11 @@ def migrate_extra_fields(extra_fields):
"model": completion_criteria.MASTERY,
}
return extra_fields


def validate_and_conform_to_schema_threshold_none(completion_criteria_validated):
model = completion_criteria_validated.get("model", {})
if model in ["reference", "determined_by_resource"]:
if "threshold" not in completion_criteria_validated or completion_criteria_validated["threshold"] is not None:
completion_criteria_validated["threshold"] = None
return completion_criteria_validated
5 changes: 5 additions & 0 deletions contentcuration/contentcuration/viewsets/contentnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from contentcuration.utils.nodes import calculate_resource_size
from contentcuration.utils.nodes import migrate_extra_fields
from contentcuration.utils.pagination import ValuesViewsetCursorPagination
from contentcuration.utils.nodes import validate_and_conform_to_schema_threshold_none
from contentcuration.viewsets.base import BulkListSerializer
from contentcuration.viewsets.base import BulkModelSerializer
from contentcuration.viewsets.base import BulkUpdateMixin
Expand Down Expand Up @@ -281,6 +282,10 @@ class CompletionCriteriaSerializer(JSONFieldDictSerializer):
model = CharField()
learner_managed = BooleanField(required=False, allow_null=True)

def update(self, instance, validated_data):
validated_data = validate_and_conform_to_schema_threshold_none(validated_data)
return super(CompletionCriteriaSerializer, self).update(instance, validated_data)


class ExtraFieldsOptionsSerializer(JSONFieldDictSerializer):
modality = ChoiceField(choices=(("QUIZ", "Quiz"),), allow_null=True, required=False)
Expand Down