This commit is contained in:
sebastien committed 2026-10-21 22:28:27 +02:00
1 parent 0d8970061e
commit c30aac0572
14 files changed
+343 -48

No files matched your search

+49 -8
View File
@@ -32,7 +32,7 @@ def fetch_and_save_sub_text(ex_id, indices, label, text_path):
try:
with urllib.request.urlopen(url) as response:
content = response.read().decode('utf-8')
content = replace_dots(content.strip("\n"))
content = prepare_latex_fragment(content.strip("\n"))
with open(os.path.join(text_path, f"{label}.tex"), 'w', encoding='utf-8') as f:
f.write(content)
# Compile PDF
@@ -52,7 +52,7 @@ def fetch_and_save_sub_sol(ex_id, indices, label, sol_path):
try:
with urllib.request.urlopen(url) as response:
content = response.read().decode('utf-8')
content = replace_dots(content.strip("\n"))
content = prepare_latex_fragment(content.strip("\n"))
with open(os.path.join(sol_path, f"{label}.tex"), 'w', encoding='utf-8') as f:
f.write(content)
# Compile PDF
@@ -70,13 +70,39 @@ def replace_dots(text):
# (?m) enables multiline mode so ^ matches start of each line
return re.sub(r"(?m)^(\s*.)\.", r"\1)", text)
def replace_problem_labels(text):
def prepare_latex_fragment(text):
"""Keep counter-only extracted lists valid before an enumitem resume."""
# Extraction can omit every item before a context block while retaining
# the list and its counters for resume*. An unnumbered item makes that
# empty list legal without advancing the resumed question number.
return re.sub(
r'(\\begin\{enumerate\}(?:\[[^\]\n]*\])?)'
r'(\s*(?:\\stepcounter\{[^{}]+\}\s*)*)'
r'(\\end\{enumerate\})',
r'\1\\item[]\2\3',
replace_dots(text),
)
def replace_problem_labels(text, numbering="problem"):
"""Replaces labels according to spaces depth when problem=True."""
def repl(m):
spaces = m.group(1)
label = m.group(2)
n = len(spaces)
try:
if numbering == "problem_abc":
if n == 1 and label.isdigit():
return f"{spaces}{ROMANS_CAP[int(label)]})"
if n == 4 and label.isalpha():
return f"{spaces}{label.upper()})"
if n == 7 and label.isdigit():
return m.group(0)
if n == 10 and label.isdigit():
return f"{spaces}{chr(96 + int(label))})"
if n == 13 and label.isdigit():
return f"{spaces}{ROMANS_LOW[int(label)]})"
return m.group(0)
if n == 1 and label.isdigit(): # 1 space: 1) -> I)
return f"{spaces}{ROMANS_CAP[int(label)]})"
elif n == 4 and label.isalpha(): # 4 spaces: a) -> 1)
@@ -92,7 +118,17 @@ def replace_problem_labels(text):
# Matches start of line, spaces, alphanumeric label, and closing parenthesis
return re.sub(r"(?m)^([ \t]+)([a-zA-Z0-9]+)\)", repl, text)
def format_indices(indices, problem=False):
def format_indices(indices, problem=False, *, numbering=None):
if numbering is not None:
if numbering not in {"exercise", "problem", "problem_abc"}:
raise ValueError(f"Unknown SHEETINFO numbering scheme: {numbering!r}")
problem = numbering != "exercise"
if numbering == "problem_abc":
formatters = (lambda n: ROMANS_CAP[n], lambda n: chr(64 + n), str,
lambda n: chr(96 + n), lambda n: ROMANS_LOW[n])
if len(indices) > len(formatters):
raise ValueError("Too many question levels for problem_abc numbering")
return "".join(formatter(index) + ")" for formatter, index in zip(formatters, indices))
if not indices: return ""
if not problem:
res = f"{indices[0]})"
@@ -200,6 +236,11 @@ def process_directory(workspace: EvaluationWorkspace) -> ExitCode:
try:
data = json.loads(json_str)
numbering = data.get('numbering')
if numbering is not None:
# Validate even exercises without question indexes.
format_indices([], numbering=numbering)
problem = numbering != 'exercise'
block_labels = []
# Construct 'ids' parameter
ex_id = str(data['id'])
@@ -222,7 +263,7 @@ def process_directory(workspace: EvaluationWorkspace) -> ExitCode:
fetch_and_save_sub_sol(ids, [], label, paths['Sol2'])
else:
for item in indexes:
suffix = format_indices(item['indices'], problem)
suffix = format_indices(item['indices'], problem, numbering=numbering)
label = f"Ex {current_ex_num}" + (f" : {suffix}" if suffix else "")
validate_windows_labels([label])
block_labels.append(label)
@@ -252,9 +293,9 @@ def process_directory(workspace: EvaluationWorkspace) -> ExitCode:
# Apply hierarchy depth replace if problem context
if problem:
t_text = replace_problem_labels(t_text)
s_text = replace_problem_labels(s_text)
p_text = replace_problem_labels(p_text)
t_text = replace_problem_labels(t_text, numbering or 'problem')
s_text = replace_problem_labels(s_text, numbering or 'problem')
p_text = replace_problem_labels(p_text, numbering or 'problem')
base_filename = f"Ex {current_ex_num}"