Skip to content

Comments

Courses, Units, Lessons, and KPUB#653

Draft
rtibbles wants to merge 3 commits intolearningequality:mainfrom
rtibbles:of_course
Draft

Courses, Units, Lessons, and KPUB#653
rtibbles wants to merge 3 commits intolearningequality:mainfrom
rtibbles:of_course

Conversation

@rtibbles
Copy link
Member

@rtibbles rtibbles commented Jan 20, 2026

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

Copy link
Member

@bjester bjester left a comment

Choose a reason for hiding this comment

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

Needs to update the le_utils version

@rtibbles
Copy link
Member Author

rtibbles commented Feb 8, 2026

Updated!

Comment on lines +276 to +288
"""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)
Copy link
Member

Choose a reason for hiding this comment

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

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.

"""
# 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):
Copy link
Member

Choose a reason for hiding this comment

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

I believe this regex would pass with just \t or \n

Comment on lines +1667 to +1671
@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
Copy link
Member

Choose a reason for hiding this comment

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

I presume this is for consistency with ExerciseNode, but they have no shared interface, besides declaring this property.


def test_variants_are_distinct(self):
"""VARIANT_A and VARIANT_B have different values."""
assert VARIANT_A != VARIANT_B
Copy link
Member

Choose a reason for hiding this comment

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

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)
Copy link
Member

Choose a reason for hiding this comment

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

Similar to the other kpub tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Node classes for Lessons, Units, and Courses

2 participants