Courses, Units, Lessons, and KPUB#653
Courses, Units, Lessons, and KPUB#653rtibbles wants to merge 4 commits intolearningequality:mainfrom
Conversation
bjester
left a comment
There was a problem hiding this comment.
Needs to update the le_utils version
|
Updated! |
tests/pipeline/test_convert.py
Outdated
| """A file with .kpub extension that isn't a valid zip should fail.""" | ||
| temp_file = tempfile.NamedTemporaryFile(suffix=".kpub", delete=False) | ||
| temp_file.write(b"not a zip file") | ||
| temp_file.close() | ||
|
|
||
| try: | ||
| handler = KPUBConversionHandler() | ||
| with pytest.raises(InvalidFileException) as exc_info: | ||
| handler.validate_archive(temp_file.name) | ||
|
|
||
| assert "zip" in str(exc_info.value).lower() | ||
| finally: | ||
| os.unlink(temp_file.name) |
There was a problem hiding this comment.
I presume these are LLM generated?
6 out of 7 tests follow pretty much the same pattern, with this test being the outlier. A test case would eliminate the redundancy.
Although, the approach is somewhat poor. It could use a context manager without delete=False and eliminate the try / finally / unlink. With that change, a test case isn't quite the dramatic cleanup.
There was a problem hiding this comment.
Yes - sorry, clearly I forgot to tag this, and I am now remembering that I never came around and did another pass over the code, just pushed the draft once I had the test channels in place.
Will rejig the tests, then double check the implementation.
ricecooker/classes/curriculum.py
Outdated
| """ | ||
| # Text must be non-empty and not just whitespace | ||
| # Pattern from le_utils schema: ^\s*\S[\s\S]*$ | ||
| if not self.text or not re.match(r"^\s*\S[\s\S]*$", self.text): |
There was a problem hiding this comment.
I believe this regex would pass with just \t or \n
ricecooker/classes/nodes.py
Outdated
| @questions.setter | ||
| def questions(self, value): | ||
| """Setter to allow base Node class initialization (ignored for UnitNode).""" | ||
| # UnitNode manages questions through test_questions, so ignore base class assignment | ||
| pass |
There was a problem hiding this comment.
I presume this is for consistency with ExerciseNode, but they have no shared interface, besides declaring this property.
tests/test_curriculum.py
Outdated
|
|
||
| def test_variants_are_distinct(self): | ||
| """VARIANT_A and VARIANT_B have different values.""" | ||
| assert VARIANT_A != VARIANT_B |
There was a problem hiding this comment.
Surely a code review is sufficient for this 😅
| assert result.content_node_metadata is not None | ||
| assert result.content_node_metadata["kind"] == content_kinds.DOCUMENT | ||
| finally: | ||
| os.unlink(temp_archive.name) |
There was a problem hiding this comment.
Similar to the other kpub tests
|
Have reworked and deduplicated the tests here. |
bjester
left a comment
There was a problem hiding this comment.
Overall everything looks good enough for merge, but I think it'd be worthwhile to match exception messages in TestUnitNodeValidation considering the complexity of the validation. And so for that, at least, I'm requesting changes.
| self._temp_dir = tempfile.TemporaryDirectory(prefix="curriculum_kpub_") | ||
| self.temp_dir = self._temp_dir.name |
There was a problem hiding this comment.
Not a big deal since this is an example, but I'm assuming self._temp_dir doesn't need to be set on the class? Looks a little weird.
There was a problem hiding this comment.
Yeah, not needed at all.
ricecooker/classes/curriculum.py
Outdated
|
|
||
| def validate(self): | ||
| """Validate the learning objective against the le_utils schema.""" | ||
| return bool(self.text and self.text.strip()) |
There was a problem hiding this comment.
The UUID generation in the constructor should fail if text is None, so is there use for self.text too?
There was a problem hiding this comment.
It won't fail if text is an empty or all whitespace string - but I've moved this into init.py to raise earlier.
ricecooker/classes/nodes.py
Outdated
| if not learning_objectives: | ||
| raise InvalidNodeException( | ||
| "Question must have at least one learning objective" | ||
| ) |
There was a problem hiding this comment.
Seems add_child and add_question both validate this and both call _validate_learning_objectives. Any reason not to add it there?
There was a problem hiding this comment.
Nope - made the error message more generic and moved it.
| Args: | ||
| node: LessonNode to add | ||
| learning_objectives: List of LearningObjective instances |
There was a problem hiding this comment.
Seems ricecooker has inconsistent docstring styles...
There was a problem hiding this comment.
Yes, they're all over the shop - I think we should just decide what our standard is across our repos and then standardize.
tests/test_curriculum.py
Outdated
| unit = UnitNode(source_id="unit-1", title="Math Unit") | ||
| lesson = LessonNode(source_id="lesson-1", title="Lesson 1") | ||
| lo = LearningObjective("Understand addition") | ||
| unit.add_child(lesson, learning_objectives=[lo]) |
There was a problem hiding this comment.
The tests here are passing this as a kwarg, which I know is okay, but I think this is considered bad practice?
There was a problem hiding this comment.
Yes, updated to just pass positionally.
tests/test_curriculum.py
Outdated
| unit.add_question(make_question("q5"), VARIANT_B, learning_objectives=[lo1]) | ||
| unit.add_question(make_question("q6"), VARIANT_B, learning_objectives=[lo2]) | ||
|
|
||
| with pytest.raises(InvalidNodeException): |
There was a problem hiding this comment.
These tests look like good coverage, but I think it would provide more certainty that the validation cases are hitting the right spots with some exception message matching for each case.
There was a problem hiding this comment.
Added message matching to all to check this.
html5lib sets body.text to None when the body element starts with a child element rather than text. Guard against this the same way as KPUBConversionHandler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Updated! |
Summary
Adds support for uploading KPUB files
Adds Courses, Units, and Lessons nodes with validation
Adds example courses script for two sample courses.
References
Fixes #638
Reviewer guidance