Skip to content
Open
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
28 changes: 28 additions & 0 deletions components/rsptx/db/crud/question.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from typing import List, Optional, Tuple, Dict
from sqlalchemy import select, and_, or_, func, asc, desc, not_, update, delete
from sqlalchemy.exc import IntegrityError
Expand Down Expand Up @@ -706,6 +707,24 @@ async def update_question(question: QuestionValidator) -> QuestionValidator:
return question


def _natural_sort_key(value: Optional[str]) -> List[Tuple[int, object]]:
"""Key for natural ordering of exercise numbers like "Q3.10.1" or "Exercise 3.1.2".

Splits the string into alternating text/number chunks so numeric runs compare
numerically instead of character-by-character (e.g. "3.2.1" sorts before "3.10.1",
where a plain string sort would put "3.10.1" first). Each chunk is tagged with a
type flag so chunks of different types never get compared directly, which would
otherwise raise a TypeError.
"""
if not value:
return [(1, "")]
return [
(0, int(chunk)) if chunk.isdigit() else (1, chunk.lower())
for chunk in re.split(r"(\d+)", value)
if chunk
]


async def fetch_questions_for_chapter_subchapter(
base_course: str,
skipreading: bool = False,
Expand Down Expand Up @@ -808,6 +827,15 @@ async def fetch_questions_for_chapter_subchapter(
del q.question
questions[c.chapter_label][sc.sub_chapter_label].append(q)

# The SQL query above orders by Question.qnumber as a plain string, which
# sorts "3.10.1" before "3.2.1". Re-sort each subchapter's questions with a
# natural sort key so multi-part exercise numbers order the way a human expects.
for chapter_questions in questions.values():
for subchapter_questions in chapter_questions.values():
subchapter_questions.sort(
key=lambda q: _natural_sort_key(q.qnumber or q.name)
)
Comment on lines +833 to +837

# Now create the hierarchical json structure where the keys are the chapter and subchapter labels
# This is the structure that is used by the React TreeTable component
def find_page_id(chapter, subchapter):
Expand Down
Loading