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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ migrate:
# 4) Remove the management command from this `deploy-migrate` recipe
# 5) Repeat!
deploy-migrate:
echo "Nothing to do here!"
python contentcuration/manage.py fix_exercise_extra_fields

contentnodegc:
python contentcuration/manage.py garbage_collect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def handle(self, *args, **options):
migrated_complete = 0
old_style_fixed = 0
old_style_complete = 0
incomplete_fixed = 0
exercises_checked = 0

for node in queryset.iterator(chunk_size=CHUNKSIZE):
Expand All @@ -77,6 +78,8 @@ def handle(self, *args, **options):
migrated_fixed += 1
if complete:
migrated_complete += 1
elif fix_type == "incomplete" and complete:
incomplete_fixed += 1
exercises_checked += 1
if exercises_checked % CHUNKSIZE == 0:
logging.info(
Expand All @@ -92,6 +95,11 @@ def handle(self, *args, **options):
migrated_complete, migrated_fixed
)
)
logging.info(
"{} marked complete that were previously incomplete".format(
incomplete_fixed
)
)

logging.info("{} / {} exercises checked".format(exercises_checked, total))
logging.info(
Expand All @@ -104,18 +112,26 @@ def handle(self, *args, **options):
migrated_complete, migrated_fixed
)
)
logging.info(
"{} marked complete that were previously incomplete".format(
incomplete_fixed
)
)
logging.info(
"Done in {:.1f}s. Fixed {} migrated exercises, "
"migrated {} old-style exercises.{}".format(
"migrated {} old-style exercises."
"marked {} previously incomplete exercises complete. {}".format(
time.time() - start,
migrated_fixed,
old_style_fixed,
incomplete_fixed,
" (dry run)" if dry_run else "",
)
)

def _process_node(self, node, dry_run):
ef = node.extra_fields
was_complete = node.complete
if isinstance(ef, str):
try:
ef = json.loads(ef)
Expand All @@ -131,6 +147,8 @@ def _process_node(self, node, dry_run):
ef["options"]["completion_criteria"]["threshold"]["m"] = None
ef["options"]["completion_criteria"]["threshold"]["n"] = None
fix_type = "m_n_fix"
elif not was_complete:
fix_type = "incomplete"
else:
return None, None
node.extra_fields = ef
Expand Down
26 changes: 26 additions & 0 deletions contentcuration/contentcuration/tests/test_contentnodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,32 @@ def test_dry_run_does_not_modify(self):
self.assertEqual(threshold["m"], 0)
self.assertEqual(threshold["n"], 0)

def test_incomplete_node_with_valid_fields_gets_marked_complete(self):
"""An incomplete exercise with valid extra_fields should be marked complete."""
node = self._create_exercise(
{
"options": {
"completion_criteria": {
"threshold": {
"mastery_model": exercises.DO_ALL,
"m": None,
"n": None,
},
"model": completion_criteria.MASTERY,
}
},
}
)
# Force incomplete status even though fields are valid
node.complete = False
node.save()

command = FixExerciseExtraFieldsCommand()
command.handle()

node.refresh_from_db()
self.assertTrue(node.complete)

def test_migrates_string_extra_fields(self):
"""Command should parse and migrate extra_fields stored as a JSON string."""
node = self._create_exercise(
Expand Down