Skip to content

Commit 85fdc12

Browse files
committed
renamed all use of module with set
1 parent b2e7827 commit 85fdc12

File tree

15 files changed

+217
-83
lines changed

15 files changed

+217
-83
lines changed

.metals/metals.lock.db

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#FileLock
2-
#Mon Jul 28 16:21:32 BST 2025
2+
#Tue Jul 29 17:03:01 BST 2025
33
hostName=localhost
44
id=1984368cdad49ef6310e31a8ad975c2da3ea6a244d8
55
method=file

docs/source/contributing/documentation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,5 @@ The [CLT reference](../reference/command-line) is generated based on the output
5050
[API documentation](../reference/library) is built based on [docstrings](https://peps.python.org/pep-0257/#what-is-a-docstring) found within the code base. This is done using [sphinx.ext.autosummary](https://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html).
5151

5252
:::{tip}
53-
See the [Module docs](../reference/_autosummary/in2lambda.api.module.Module) and the source code buttons on its page for good examples on how to write the docstrings effectively.
53+
See the [Set docs](../reference/_autosummary/in2lambda.api.set.Set) and the source code buttons on its page for good examples on how to write the docstrings effectively.
5454
:::

in2lambda/api/module.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111

1212
@dataclass
13-
class Module:
13+
class Set:
1414
"""Represents a list of questions."""
1515

1616
questions: list[Question] = field(default_factory=list)
@@ -27,12 +27,12 @@ def current_question(self) -> Question:
2727
The current question or Question("INVALID") if there are no questions.
2828
2929
Examples:
30-
>>> from in2lambda.api.module import Module
31-
>>> Module().current_question
30+
>>> from in2lambda.api.set import Set
31+
>>> Set().current_question
3232
Question(title='INVALID', parts=[], images=[], main_text='')
33-
>>> module = Module()
34-
>>> module.add_question()
35-
>>> module.current_question
33+
>>> s = Set()
34+
>>> s.add_question()
35+
>>> s.current_question
3636
Question(title='', parts=[], images=[], main_text='')
3737
"""
3838
return (
@@ -44,22 +44,22 @@ def current_question(self) -> Question:
4444
def add_question(
4545
self, title: str = "", main_text: Union[pf.Element, str] = pf.Str("")
4646
) -> None:
47-
"""Inserts a new question into the module.
47+
"""Inserts a new question into the set.
4848
4949
Args:
5050
title: An optional string for the title of the question. If no title
5151
is provided, the question title auto-increments i.e. Question 1, 2, etc.
5252
main_text: An optional string or panflute element for the main question text.
5353
5454
Examples:
55-
>>> from in2lambda.api.module import Module
55+
>>> from in2lambda.api.set import Set
5656
>>> import panflute as pf
57-
>>> module = Module()
58-
>>> module.add_question("Some title", pf.Para(pf.Str("hello"), pf.Space, pf.Str("there")))
59-
>>> module
60-
Module(questions=[Question(title='Some title', parts=[], images=[], main_text='hello there')])
61-
>>> module.add_question(main_text="Normal string text")
62-
>>> module.questions[1].main_text
57+
>>> s = Set()
58+
>>> s.add_question("Some title", pf.Para(pf.Str("hello"), pf.Space, pf.Str("there")))
59+
>>> s
60+
Set(questions=[Question(title='Some title', parts=[], images=[], main_text='hello there')])
61+
>>> s.add_question(main_text="Normal string text")
62+
>>> s.questions[1].main_text
6363
'Normal string text'
6464
"""
6565
question = Question(title=title)
@@ -75,24 +75,24 @@ def increment_current_question(self) -> None:
7575
The is useful if adding question text first and answers later.
7676
7777
Examples:
78-
>>> from in2lambda.api.module import Module
79-
>>> module = Module()
78+
>>> from in2lambda.api.set import Set
79+
>>> s = Set()
8080
>>> # Imagine adding the questions from a question file first...
81-
>>> module.add_question("Question 1")
82-
>>> module.add_question("Question 2")
81+
>>> s.add_question("Question 1")
82+
>>> s.add_question("Question 2")
8383
>>> # ...and then adding solutions from an answer file later
84-
>>> module.increment_current_question() # Loop back to question 1
85-
>>> module.current_question.add_solution("Question 1 answer")
86-
>>> module.increment_current_question()
87-
>>> module.current_question.add_solution("Question 2 answer")
88-
>>> module.questions
84+
>>> s.increment_current_question() # Loop back to question 1
85+
>>> s.current_question.add_solution("Question 1 answer")
86+
>>> s.increment_current_question()
87+
>>> s.current_question.add_solution("Question 2 answer")
88+
>>> s.questions
8989
[Question(title='Question 1', parts=[Part(text='', worked_solution='Question 1 answer')], images=[], main_text=''),\
9090
Question(title='Question 2', parts=[Part(text='', worked_solution='Question 2 answer')], images=[], main_text='')]
9191
"""
9292
self._current_question_index += 1
9393

9494
def to_json(self, output_dir: str) -> None:
95-
"""Turns this module into Lambda Feedback JSON/ZIP files.
95+
"""Turns this set into Lambda Feedback JSON/ZIP files.
9696
9797
WARNING: This will overwrite any existing files in the directory.
9898
@@ -103,13 +103,13 @@ def to_json(self, output_dir: str) -> None:
103103
>>> import tempfile
104104
>>> import os
105105
>>> import json
106-
>>> # Create a module with two questions
107-
>>> module = Module()
108-
>>> module.add_question("Question 1")
109-
>>> module.add_question("Question 2")
106+
>>> # Create a set with two questions
107+
>>> s = Set()
108+
>>> s.add_question("Question 1")
109+
>>> s.add_question("Question 2")
110110
>>> with tempfile.TemporaryDirectory() as temp_dir:
111111
... # Write the JSON files to the temporary directory
112-
... module.to_json(temp_dir)
112+
... s.to_json(temp_dir)
113113
... # Check the contents of the directory
114114
... sorted(os.listdir(temp_dir))
115115
... # Check the contents of the set directory

in2lambda/api/question.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""A full question with optional parts that's contained in a module."""
1+
"""A full question with optional parts that's contained in a set."""
22

33
from dataclasses import dataclass, field
44
from typing import Union

in2lambda/api/set.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""Represents a list of questions."""
2+
3+
from dataclasses import dataclass, field
4+
from typing import Union
5+
6+
import panflute as pf
7+
8+
from in2lambda.api.question import Question
9+
from in2lambda.json_convert import json_convert
10+
11+
12+
@dataclass
13+
class Set:
14+
"""Represents a list of questions."""
15+
16+
questions: list[Question] = field(default_factory=list)
17+
_current_question_index = -1
18+
19+
@property
20+
def current_question(self) -> Question:
21+
"""The current question being modified, or Question("INVALID") if there are no questions.
22+
23+
The reasoning behind returning Question("INVALID") is in case filter logic is being applied
24+
on text before the first question (e.g. intro paragraphs). In that case, there is no effect.
25+
26+
Returns:
27+
The current question or Question("INVALID") if there are no questions.
28+
29+
Examples:
30+
>>> from in2lambda.api.set import Set
31+
>>> Set().current_question
32+
Question(title='INVALID', parts=[], images=[], main_text='')
33+
>>> s = Set()
34+
>>> s.add_question()
35+
>>> s.current_question
36+
Question(title='', parts=[], images=[], main_text='')
37+
"""
38+
return (
39+
self.questions[self._current_question_index]
40+
if self.questions
41+
else Question("INVALID")
42+
)
43+
44+
def add_question(
45+
self, title: str = "", main_text: Union[pf.Element, str] = pf.Str("")
46+
) -> None:
47+
"""Inserts a new question into the set.
48+
49+
Args:
50+
title: An optional string for the title of the question. If no title
51+
is provided, the question title auto-increments i.e. Question 1, 2, etc.
52+
main_text: An optional string or panflute element for the main question text.
53+
54+
Examples:
55+
>>> from in2lambda.api.set import Set
56+
>>> import panflute as pf
57+
>>> s = Set()
58+
>>> s.add_question("Some title", pf.Para(pf.Str("hello"), pf.Space, pf.Str("there")))
59+
>>> s
60+
Set(questions=[Question(title='Some title', parts=[], images=[], main_text='hello there')])
61+
>>> s.add_question(main_text="Normal string text")
62+
>>> s.questions[1].main_text
63+
'Normal string text'
64+
"""
65+
question = Question(title=title)
66+
question.main_text = main_text
67+
self.questions.append(question)
68+
69+
def increment_current_question(self) -> None:
70+
"""Manually overrides the current question being modified.
71+
72+
The default (-1) indicates the last question added. Incrementing for the
73+
first time sets to 0 i.e. the first question.
74+
75+
The is useful if adding question text first and answers later.
76+
77+
Examples:
78+
>>> from in2lambda.api.set import Set
79+
>>> s = Set()
80+
>>> # Imagine adding the questions from a question file first...
81+
>>> s.add_question("Question 1")
82+
>>> s.add_question("Question 2")
83+
>>> # ...and then adding solutions from an answer file later
84+
>>> s.increment_current_question() # Loop back to question 1
85+
>>> s.current_question.add_solution("Question 1 answer")
86+
>>> s.increment_current_question()
87+
>>> s.current_question.add_solution("Question 2 answer")
88+
>>> s.questions
89+
[Question(title='Question 1', parts=[Part(text='', worked_solution='Question 1 answer')], images=[], main_text=''),\
90+
Question(title='Question 2', parts=[Part(text='', worked_solution='Question 2 answer')], images=[], main_text='')]
91+
"""
92+
self._current_question_index += 1
93+
94+
def to_json(self, output_dir: str) -> None:
95+
"""Turns this set into Lambda Feedback JSON/ZIP files.
96+
97+
WARNING: This will overwrite any existing files in the directory.
98+
99+
Args:
100+
output_dir: Where to output the final Lambda Feedback JSON/ZIP files.
101+
102+
Examples:
103+
>>> import tempfile
104+
>>> import os
105+
>>> import json
106+
>>> # Create a set with two questions
107+
>>> s = Set()
108+
>>> s.add_question("Question 1")
109+
>>> s.add_question("Question 2")
110+
>>> with tempfile.TemporaryDirectory() as temp_dir:
111+
... # Write the JSON files to the temporary directory
112+
... s.to_json(temp_dir)
113+
... # Check the contents of the directory
114+
... sorted(os.listdir(temp_dir))
115+
... # Check the contents of the set directory
116+
... sorted(os.listdir(f"{temp_dir}/set"))
117+
... # Check the title of the first question
118+
... with open(f"{temp_dir}/set/question_1.json") as file:
119+
... print(f"Question 1's title: {json.load(file)['title']}")
120+
['set', 'set.zip']
121+
['media', 'question_1.json', 'question_2.json']
122+
Question 1's title: Question 1
123+
124+
"""
125+
json_convert.main(self.questions, output_dir)

in2lambda/filters/PartPartSolSol/filter.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
import panflute as pf
88

9-
from in2lambda.api.module import Module
9+
from in2lambda.api.set import Set
1010
from in2lambda.filters.markdown import filter
1111

1212

1313
@filter
1414
def pandoc_filter(
1515
elem: pf.Element,
1616
doc: pf.elements.Doc,
17-
module: Module,
17+
set: Set,
1818
parsing_answers: bool,
1919
) -> Optional[pf.Str]:
2020
"""A Pandoc filter that parses and translates various TeX elements.
@@ -23,7 +23,7 @@ def pandoc_filter(
2323
elem: The current TeX element being processed. This could be a paragraph,
2424
ordered list, etc.
2525
doc: A Pandoc document container - essentially the Pandoc AST.
26-
module: The Python API that is used to store the result after processing
26+
set: The Python API that is used to store the result after processing
2727
the TeX file.
2828
parsing_answers: Whether an answers-only document is currently being parsed.
2929
@@ -46,10 +46,10 @@ def pandoc_filter(
4646

4747
# Solutions are in a Div
4848
if isinstance(elem, pf.Div):
49-
module.add_question(main_text="\n".join(pandoc_filter.question))
49+
set.add_question(main_text="\n".join(pandoc_filter.question))
5050
if hasattr(pandoc_filter, "parts"):
5151
for part in pandoc_filter.parts:
52-
module.current_question.add_part_text(part)
52+
set.current_question.add_part_text(part)
5353
pandoc_filter.question = []
5454
pandoc_filter.parts = []
5555

@@ -58,8 +58,8 @@ def pandoc_filter(
5858
match type(first_answer_part := elem.content[0]):
5959
case pf.OrderedList:
6060
for item in first_answer_part.content:
61-
module.current_question.add_solution(pf.stringify(item))
61+
set.current_question.add_solution(pf.stringify(item))
6262
case pf.Para:
63-
module.current_question.add_solution(pf.stringify(elem))
63+
set.current_question.add_solution(pf.stringify(elem))
6464

6565
return None

in2lambda/filters/PartSolPartSol/filter.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77

88
import panflute as pf
99

10-
from in2lambda.api.module import Module
10+
from in2lambda.api.set import Set
1111
from in2lambda.filters.markdown import filter
1212

1313

1414
@filter
1515
def pandoc_filter(
1616
elem: pf.Element,
1717
doc: pf.elements.Doc,
18-
module: Module,
18+
set: Set,
1919
parsing_answers: bool,
2020
) -> Optional[pf.Str]:
2121
"""A Pandoc filter that parses and translates various TeX elements.
@@ -24,7 +24,7 @@ def pandoc_filter(
2424
elem: The current TeX element being processed. This could be a paragraph,
2525
ordered list, etc.
2626
doc: A Pandoc document container - essentially the Pandoc AST.
27-
module: The Python API that is used to store the result after processing
27+
set: The Python API that is used to store the result after processing
2828
the TeX file.
2929
parsing_answers: Whether an answers-only document is currently being parsed.
3030
@@ -48,16 +48,16 @@ def pandoc_filter(
4848
for item in listItem.content
4949
if not isinstance(item, pf.Div)
5050
]
51-
module.current_question.add_part_text("\n".join(part))
52-
module.current_question.add_solution(
51+
set.current_question.add_part_text("\n".join(part))
52+
set.current_question.add_solution(
5353
pandoc_filter.solutions.popleft()
5454
)
5555

5656
if isinstance(elem, pf.Div):
5757
pandoc_filter.solutions.append(pf.stringify(elem))
5858
if pandoc_filter.question:
59-
module.add_question(main_text="\n".join(pandoc_filter.question))
60-
module.current_question.add_solution(pf.stringify(elem))
61-
module.current_question._last_part["solution"] -= 1
59+
set.add_question(main_text="\n".join(pandoc_filter.question))
60+
set.current_question.add_solution(pf.stringify(elem))
61+
set.current_question._last_part["solution"] -= 1
6262
pandoc_filter.question = []
6363
return None

0 commit comments

Comments
 (0)