in2lambda 0.3.1__py3-none-any.whl → 0.3.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
in2lambda/api/module.py CHANGED
@@ -6,6 +6,7 @@ from typing import Union
6
6
  import panflute as pf
7
7
 
8
8
  from in2lambda.api.question import Question
9
+ from in2lambda.json_convert import json_convert
9
10
 
10
11
 
11
12
  @dataclass
@@ -28,11 +29,11 @@ class Module:
28
29
  Examples:
29
30
  >>> from in2lambda.api.module import Module
30
31
  >>> Module().current_question
31
- Question(title='INVALID', parts=[], images=[], _main_text='')
32
+ Question(title='INVALID', parts=[], images=[], main_text='')
32
33
  >>> module = Module()
33
34
  >>> module.add_question()
34
35
  >>> module.current_question
35
- Question(title='', parts=[], images=[], _main_text='')
36
+ Question(title='', parts=[], images=[], main_text='')
36
37
  """
37
38
  return (
38
39
  self.questions[self._current_question_index]
@@ -56,7 +57,7 @@ class Module:
56
57
  >>> module = Module()
57
58
  >>> module.add_question("Some title", pf.Para(pf.Str("hello"), pf.Space, pf.Str("there")))
58
59
  >>> module
59
- Module(questions=[Question(title='Some title', parts=[], images=[], _main_text='hello there')])
60
+ Module(questions=[Question(title='Some title', parts=[], images=[], main_text='hello there')])
60
61
  >>> module.add_question(main_text="Normal string text")
61
62
  >>> module.questions[1].main_text
62
63
  'Normal string text'
@@ -85,7 +86,40 @@ class Module:
85
86
  >>> module.increment_current_question()
86
87
  >>> module.current_question.add_solution("Question 2 answer")
87
88
  >>> module.questions
88
- [Question(title='Question 1', parts=[Part(text='', worked_solution='Question 1 answer')], images=[], _main_text=''),\
89
- Question(title='Question 2', parts=[Part(text='', worked_solution='Question 2 answer')], images=[], _main_text='')]
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='')]
90
91
  """
91
92
  self._current_question_index += 1
93
+
94
+ def to_json(self, output_dir: str) -> None:
95
+ """Turns this module 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 module with two questions
107
+ >>> module = Module()
108
+ >>> module.add_question("Question 1")
109
+ >>> module.add_question("Question 2")
110
+ >>> with tempfile.TemporaryDirectory() as temp_dir:
111
+ ... # Write the JSON files to the temporary directory
112
+ ... module.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/api/question.py CHANGED
@@ -16,8 +16,8 @@ class Question:
16
16
 
17
17
  Examples:
18
18
  >>> from in2lambda.api.question import Question
19
- >>> Question(title="Some title", _main_text="Some text")
20
- Question(title='Some title', parts=[], images=[], _main_text='Some text')
19
+ >>> Question(title="Some title", main_text="Some text")
20
+ Question(title='Some title', parts=[], images=[], main_text='Some text')
21
21
  """
22
22
 
23
23
  title: str = ""
@@ -25,7 +25,11 @@ class Question:
25
25
  parts: list[Part] = field(default_factory=list)
26
26
  images: list[str] = field(default_factory=list)
27
27
 
28
- _main_text: str = ""
28
+ # Use getter/setter methods for main_text without exposing private attribute.
29
+ # See https://stackoverflow.com/a/61480946
30
+ main_text: str = ""
31
+ _main_text: str = field(init=False, repr=False, default="")
32
+
29
33
  _last_part: dict[str, int] = field(
30
34
  default_factory=lambda: {"solution": 0, "text": 0}, repr=False
31
35
  )
@@ -34,15 +38,10 @@ class Question:
34
38
 
35
39
  @property
36
40
  def main_text(self) -> str:
37
- """Main top-level question text."""
38
- return self._main_text
41
+ r"""Main top-level question text.
39
42
 
40
- @main_text.setter
41
- def main_text(self, value: Union[pf.Element, str]) -> None:
42
- r"""Appends to the top-level main text, which starts off as an empty string.
43
-
44
- Args:
45
- value: A panflute element or string denoting what to append to the main text.
43
+ Setting the attribute multiple times appends to the current value with a newline.
44
+ This allows question text to be dynamically appended.
46
45
 
47
46
  Examples:
48
47
  >>> from in2lambda.api.question import Question
@@ -52,7 +51,28 @@ class Question:
52
51
  >>> question.main_text
53
52
  'hello\nthere'
54
53
  """
55
- text_value = value if isinstance(value, str) else pf.stringify(value, False)
54
+ return self._main_text
55
+
56
+ @main_text.setter
57
+ def main_text(self, value: Union[pf.Element, str, property]) -> None:
58
+ r"""Appends to the top-level main text, which starts off as an empty string.
59
+
60
+ Args:
61
+ value: A panflute element or string denoting what to append to the main text.
62
+
63
+ See example in main_text property.
64
+ """
65
+ # Converts the inputted value into a string, stored in text_value
66
+ match value:
67
+ case str():
68
+ text_value = value
69
+ case property():
70
+ # Use default value when no value set at initialisation.
71
+ # See https://stackoverflow.com/a/61480946
72
+ text_value = self.main_text
73
+ case pf.Element():
74
+ text_value = pf.stringify(value, False)
75
+
56
76
  if self._main_text:
57
77
  self._main_text += "\n"
58
78
  self._main_text += text_value
@@ -69,7 +89,7 @@ class Question:
69
89
  >>> question.add_part_text("part a")
70
90
  >>> question.add_solution("part a solution")
71
91
  >>> question
72
- Question(title='', parts=[Part(text='part a', worked_solution='part a solution')], images=[], _main_text='')
92
+ Question(title='', parts=[Part(text='part a', worked_solution='part a solution')], images=[], main_text='')
73
93
  >>> question.add_part_text("part b")
74
94
  >>> question.add_part_text("part c")
75
95
  >>> question.add_solution("Solution for b")
@@ -77,12 +97,12 @@ class Question:
77
97
  >>> question
78
98
  Question(title='', parts=[Part(text='part a', worked_solution='part a solution'), \
79
99
  Part(text='part b', worked_solution='Solution for b'), \
80
- Part(text='part c', worked_solution='Solution for b')], images=[], _main_text='')
100
+ Part(text='part c', worked_solution='Solution for b')], images=[], main_text='')
81
101
  >>> question.add_solution("We now have a solution for c!")
82
102
  >>> question
83
103
  Question(title='', parts=[Part(text='part a', worked_solution='part a solution'), \
84
104
  Part(text='part b', worked_solution='Solution for b'), \
85
- Part(text='part c', worked_solution='We now have a solution for c!')], images=[], _main_text='')
105
+ Part(text='part c', worked_solution='We now have a solution for c!')], images=[], main_text='')
86
106
  """
87
107
  elem_text = elem if isinstance(elem, str) else pf.stringify(elem)
88
108
 
@@ -111,13 +131,13 @@ Part(text='part c', worked_solution='We now have a solution for c!')], images=[]
111
131
  >>> question.add_part_text("part a")
112
132
  >>> question.add_solution("part a solution")
113
133
  >>> question
114
- Question(title='', parts=[Part(text='part a', worked_solution='part a solution')], images=[], _main_text='')
134
+ Question(title='', parts=[Part(text='part a', worked_solution='part a solution')], images=[], main_text='')
115
135
  >>> # Supports adding the answer first.
116
136
  >>> question.add_solution("part b solution")
117
137
  >>> question.add_part_text("part b")
118
138
  >>> question
119
139
  Question(title='', parts=[Part(text='part a', worked_solution='part a solution'), \
120
- Part(text='part b', worked_solution='part b solution')], images=[], _main_text='')
140
+ Part(text='part b', worked_solution='part b solution')], images=[], main_text='')
121
141
  """
122
142
  elem_text = elem if isinstance(elem, str) else pf.stringify(elem)
123
143
 
Binary file
Binary file
Binary file
@@ -72,7 +72,9 @@ def pandoc_filter(
72
72
  # Add each part solution/text
73
73
  # For the solution, prepend any top level answer text to each part answer
74
74
  for part in lettered_parts:
75
- module.current_question.add_solution(
76
- spaced_blurb + part
77
- ) if parsing_answers else module.current_question.add_part_text(part)
75
+ (
76
+ module.current_question.add_solution(spaced_blurb + part)
77
+ if parsing_answers
78
+ else module.current_question.add_part_text(part)
79
+ )
78
80
  return None
@@ -32,12 +32,12 @@ def image_directories(tex_file: str) -> list[str]:
32
32
  >>> temp_dir = tempfile.mkdtemp()
33
33
  >>> tex_file = os.path.join(temp_dir, 'test.tex')
34
34
  >>> with open(tex_file, 'w') as f:
35
- ... f.write("\graphicspath{{subdir1/}{subdir2/}{subdir3/}}")
35
+ ... f.write("\\graphicspath{{subdir1/}{subdir2/}{subdir3/}}")
36
36
  45
37
37
  >>> image_directories(tex_file)
38
38
  ['subdir1/', 'subdir2/', 'subdir3/']
39
39
  >>> with open(tex_file, 'w') as f:
40
- ... f.write("\graphicspath{ { subdir1/ }, { subdir2/ }, { subdir3/ } }")
40
+ ... f.write("\\graphicspath{ { subdir1/ }, { subdir2/ }, { subdir3/ } }")
41
41
  57
42
42
  >>> image_directories.cache_clear()
43
43
  >>> image_directories(tex_file)
@@ -78,7 +78,7 @@ def image_path(image_name: str, tex_file: str) -> Optional[str]:
78
78
  >>> temp_dir = tempfile.mkdtemp()
79
79
  >>> tex_file = os.path.join(temp_dir, 'test.tex')
80
80
  >>> with open(tex_file, 'w') as f:
81
- ... f.write("\graphicspath{{./subdir1/}{./subdir2/}{./subdir3/}}")
81
+ ... f.write("\\graphicspath{{./subdir1/}{./subdir2/}{./subdir3/}}")
82
82
  51
83
83
  >>> # Example image in a relative subdirectory
84
84
  >>> sub_dir = os.path.join(temp_dir, 'subdir3')
@@ -120,7 +120,10 @@ def filter(
120
120
  [pf.Element, pf.elements.Doc, Module, bool],
121
121
  Optional[pf.Str],
122
122
  ]
123
- ) -> Callable[[pf.Element, pf.elements.Doc, Module, str, bool], Optional[pf.Str],]:
123
+ ) -> Callable[
124
+ [pf.Element, pf.elements.Doc, Module, str, bool],
125
+ Optional[pf.Str],
126
+ ]:
124
127
  """Python decorator to make generic LaTeX elements markdown readable.
125
128
 
126
129
  As an example, part of the process involves putting dollar signs around maths
@@ -24,6 +24,15 @@ def converter(
24
24
  """
25
25
  # Create output by copying template
26
26
 
27
+ # create directory to put the questions
28
+ os.makedirs(output_dir, exist_ok=True)
29
+ output_question = os.path.join(output_dir, "set")
30
+ os.makedirs(output_question, exist_ok=True)
31
+
32
+ # create directory to put images - should be in set
33
+ output_image = os.path.join(output_question, "media")
34
+ os.makedirs(output_image, exist_ok=True)
35
+
27
36
  for i in range(len(ListQuestions)):
28
37
  output = deepcopy(template)
29
38
 
@@ -39,27 +48,19 @@ def converter(
39
48
  # add parts to the question file
40
49
  if ListQuestions[i].parts:
41
50
  output["parts"][0]["content"] = ListQuestions[i].parts[0].text
42
- output["parts"][0]["workedSolution"][0]["content"] = (
51
+ output["parts"][0]["workedSolution"]["content"] = (
43
52
  ListQuestions[i].parts[0].worked_solution
44
53
  )
45
54
  for j in range(1, len(ListQuestions[i].parts)):
46
55
  output["parts"].append(deepcopy(template["parts"][0]))
47
56
  output["parts"][j]["content"] = ListQuestions[i].parts[j].text
48
- output["parts"][j]["workedSolution"][0]["content"] = (
57
+ output["parts"][j]["orderNumber"] = j
58
+ output["parts"][j]["workedSolution"]["content"] = (
49
59
  ListQuestions[i].parts[j].worked_solution
50
60
  )
51
61
 
52
62
  # Output file
53
- filename = "Question " + str(i + 1)
54
-
55
- # create directory to put the questions
56
- os.makedirs(output_dir, exist_ok=True)
57
- output_question = os.path.join(output_dir, filename)
58
- os.makedirs(output_question, exist_ok=True)
59
-
60
- # create directory to put image
61
- output_image = os.path.join(output_question, "media")
62
- os.makedirs(output_image, exist_ok=True)
63
+ filename = "question_" + str(i + 1)
63
64
 
64
65
  # write questions into directory
65
66
  with open(f"{output_question}/{filename}.json", "w") as file:
@@ -1,26 +1,23 @@
1
1
  {
2
- "isPublished": false,
2
+ "displayFinalAnswer": true,
3
+ "displayStructuredTutorial": true,
4
+ "displayWorkedSolution": true,
3
5
  "masterContent": "Top level question here",
4
6
  "parts": [
5
7
  {
6
8
  "answer": "",
7
9
  "content": "Part text here",
8
- "responseArea": [],
10
+ "orderNumber": 0,
11
+ "responseAreas": [],
9
12
  "tutorial": [],
10
13
  "universalPartId": "N/A",
11
- "workedSolution": [
12
- {
13
- "content": "Part worked solution here",
14
- "id": "N/A",
15
- "title": ""
16
- }
17
- ]
14
+ "workedSolution": {
15
+ "content": "Part worked solution here",
16
+ "id": "N/A",
17
+ "title": ""
18
+ }
18
19
  }
19
20
  ],
20
- "questionSettings": {
21
- "displayFinalAnswer": true,
22
- "displayStructuredTutorial": true,
23
- "displayWorkedSolution": true
24
- },
21
+ "publish": false,
25
22
  "title": "Question title here"
26
23
  }
in2lambda/main.py CHANGED
@@ -1,7 +1,14 @@
1
1
  """The main input for in2lambda, defining both the CLT and main library function."""
2
2
 
3
+ # This commented block makes it run the local files rather than the pip library (I think, I don't understand it. Kevin wrote it.)
4
+ #
5
+ # import sys
6
+ # import os
7
+ # sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
8
+
3
9
  import importlib
4
10
  import pkgutil
11
+ import subprocess
5
12
  from typing import Optional
6
13
 
7
14
  import panflute as pf
@@ -9,7 +16,19 @@ import rich_click as click
9
16
 
10
17
  import in2lambda.filters
11
18
  from in2lambda.api.module import Module
12
- from in2lambda.json_convert import json_convert
19
+
20
+
21
+ def docx_to_md(docx_file: str) -> str:
22
+ """Converts .docx files to markdown.
23
+
24
+ Args:
25
+ docx_file: A file path with the file extension included.
26
+
27
+ Returns:
28
+ the contents of the .docx file in markdown formatting
29
+ """
30
+ md_output = subprocess.check_output(["pandoc", docx_file, "-t", "markdown"])
31
+ return md_output.decode("utf-8")
13
32
 
14
33
 
15
34
  def file_type(file: str) -> str:
@@ -40,10 +59,20 @@ def file_type(file: str) -> str:
40
59
  match (extension := file.split(".")[-1].lower()):
41
60
  case "tex" | "latex" | "ltx":
42
61
  return "latex"
43
- case "md" | "rmd" | "markdown" | "mdown" | "mdwn" | "mkd" | "mkdn" | "text" | "txt":
62
+ case (
63
+ "md"
64
+ | "rmd"
65
+ | "markdown"
66
+ | "mdown"
67
+ | "mdwn"
68
+ | "mkd"
69
+ | "mkdn"
70
+ | "text"
71
+ | "txt"
72
+ ):
44
73
  return "markdown"
45
74
  case "docx":
46
- return "docx" # Pandoc doesn't seem to support doc
75
+ return "docx" # Pandoc doesn't seem to support .doc, and panflute doesn't like .docx.
47
76
  raise RuntimeError(f"Unsupported file extension: .{extension}")
48
77
 
49
78
 
@@ -71,9 +100,9 @@ def runner(
71
100
  >>> from in2lambda.main import runner
72
101
  >>> # Retrieve an example TeX file and run the given filter.
73
102
  >>> runner(f"{os.path.dirname(in2lambda.__file__)}/filters/PartsSepSol/example.tex", "PartsSepSol") # doctest: +ELLIPSIS
74
- Module(questions=[Question(title='', parts=[Part(text=..., worked_solution=''), ...], images=[], _main_text='This is a sample question\n\n'), ...])
103
+ Module(questions=[Question(title='', parts=[Part(text=..., worked_solution=''), ...], images=[], main_text='This is a sample question\n\n'), ...])
75
104
  >>> runner(f"{os.path.dirname(in2lambda.__file__)}/filters/PartsOneSol/example.tex", "PartsOneSol") # doctest: +ELLIPSIS
76
- Module(questions=[Question(title='', parts=[Part(text='This is part (a)\n\n', worked_solution=''), ...], images=[], _main_text='Here is some preliminary question information that might be useful.'), ...)
105
+ Module(questions=[Question(title='', parts=[Part(text='This is part (a)\n\n', worked_solution=''), ...], images=[], main_text='Here is some preliminary question information that might be useful.'), ...)
77
106
  """
78
107
  # The list of questions for Lambda Feedback as a Python API.
79
108
  module = Module()
@@ -81,15 +110,20 @@ def runner(
81
110
  # Dynamically import the correct pandoc filter depending on the subject.
82
111
  filter_module = importlib.import_module(f"in2lambda.filters.{chosen_filter}.filter")
83
112
 
84
- with open(question_file, "r", encoding="utf-8") as file:
85
- text = file.read()
113
+ if file_type(question_file) == "docx":
114
+ # Convert .docx to md using Pandoc and proceed
115
+ text = docx_to_md(question_file)
116
+ input_format = "markdown"
117
+ else:
118
+ with open(question_file, "r", encoding="utf-8") as file:
119
+ text = file.read()
120
+
121
+ input_format = file_type(question_file)
86
122
 
87
123
  # Parse the Pandoc AST using the relevant panflute filter.
88
124
  pf.run_filter(
89
125
  filter_module.pandoc_filter,
90
- doc=pf.convert_text(
91
- text, input_format=file_type(question_file), standalone=True
92
- ),
126
+ doc=pf.convert_text(text, input_format=input_format, standalone=True),
93
127
  module=module,
94
128
  tex_file=question_file,
95
129
  parsing_answers=False,
@@ -97,13 +131,20 @@ def runner(
97
131
 
98
132
  # If separate answer TeX file provided, parse that as well.
99
133
  if answer_file:
100
- with open(answer_file, "r", encoding="utf-8") as file:
101
- answer_text = file.read()
134
+
135
+ if file_type(answer_file) == "docx":
136
+
137
+ answer_text = docx_to_md(answer_file)
138
+ answer_format = "markdown"
139
+ else:
140
+ with open(answer_file, "r", encoding="utf-8") as file:
141
+ answer_text = file.read()
142
+ answer_format = file_type(answer_file)
102
143
 
103
144
  pf.run_filter(
104
145
  filter_module.pandoc_filter,
105
146
  doc=pf.convert_text(
106
- answer_text, input_format=file_type(answer_file), standalone=True
147
+ answer_text, input_format=answer_format, standalone=True
107
148
  ),
108
149
  module=module,
109
150
  tex_file=answer_file,
@@ -112,7 +153,7 @@ def runner(
112
153
 
113
154
  # Read the Python API format and convert to JSON.
114
155
  if output_dir is not None:
115
- json_convert.main(module.questions, output_dir)
156
+ module.to_json(output_dir)
116
157
 
117
158
  return module
118
159
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: in2lambda
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Converts content ready for import into Lambda Feedback
5
5
  Home-page: https://lambda-feedback.github.io/in2lambda
6
6
  License: MIT
@@ -12,11 +12,12 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
15
16
  Classifier: Programming Language :: Python :: 3 :: Only
16
17
  Classifier: Typing :: Typed
17
- Requires-Dist: beartype (>=0.16.4,<0.17.0)
18
- Requires-Dist: panflute (>=2.3.0,<3.0.0)
19
- Requires-Dist: rich-click (>=1.7.2,<2.0.0)
18
+ Requires-Dist: beartype (>=0.17.2,<0.18.0)
19
+ Requires-Dist: panflute (>=2.3.1,<3.0.0)
20
+ Requires-Dist: rich-click (>=1.7.4,<2.0.0)
20
21
  Project-URL: Bug Tracker, https://github.com/lambda-feedback/in2lambda/issues
21
22
  Project-URL: Documentation, https://lambda-feedback.github.io/in2lambda
22
23
  Project-URL: Repository, https://github.com/lambda-feedback/in2lambda
@@ -1,11 +1,13 @@
1
1
  in2lambda/__init__.py,sha256=VvbVGkrakfqTIqnYUuv95_LFxitkMODRYr161R9128U,492
2
2
  in2lambda/api/__init__.py,sha256=uh1WA1JIIYmjaWXms3JR5LOlnizv-ozW4ijBHQxi9QA,103
3
- in2lambda/api/module.py,sha256=pxAYA355aY5B07JTCDetdP9U0GiXCMh-aWRc9QAiPAE,3729
3
+ in2lambda/api/module.py,sha256=frAU0m6dJceXiVlGKiny_1UJGowwO0Q6p6RAlKjKqME,5173
4
4
  in2lambda/api/part.py,sha256=Y-W0sd5mJQifA8V8nZNTYk_HPhxhRQq5mUCdlYKioXw,204
5
- in2lambda/api/question.py,sha256=EJpg8dZEj3byTTQNXu0QIHH1IqRh75ZjWs39E1ninfU,5408
6
- in2lambda/example.docx,sha256=EtGDjV3343ev_WKrnkKN5cPG1lGGfMtKZx5k6jyOAvA,32824
5
+ in2lambda/api/question.py,sha256=is-Bmx85GFAV7Jf8mblP2ZtkDgfbRcVbMhh9Tmy2hmk,6173
7
6
  in2lambda/example_questions.tex,sha256=kvcuUUoQXTJYqrAlUMfibzGsL9qFgqbKS57JIg1YhkA,9491
8
7
  in2lambda/example_solutions.tex,sha256=0idJQV9Q8VhMO_dB5RYzYkZzyEMSA594sqnPdl4wlLA,22164
8
+ in2lambda/figures/O2_potential.png,sha256=AxDizIID24J20wqVUAneGIN_WWr6Vfra3xoBdkgJbK0,64855
9
+ in2lambda/figures/activated_complex.png,sha256=6AQuS6lftVMX0GkAR5WpOQnSgzvhNA3Pb2c2iteUieE,265793
10
+ in2lambda/figures/rocket-momentum.png,sha256=EXsR7k_WehhSBfnsh4fssCTEKHpu6q3lCa4csHAFGHI,397693
9
11
  in2lambda/filters/PartPartSolSol/__init__.py,sha256=YJCnCoGJfXt7jrMdgcsEfkI7Su9WK-VOvUwwefQ8zog,124
10
12
  in2lambda/filters/PartPartSolSol/example.tex,sha256=TK4JyjYj_wFfi9bkV8uKfXdFIBYPsvgMng5x1XdE_oU,667
11
13
  in2lambda/filters/PartPartSolSol/filter.py,sha256=vttP0_RNjLuLu2GSUudzv2qk-AQHuRb172JAwf_0mmA,2383
@@ -17,23 +19,20 @@ in2lambda/filters/PartsOneSol/example.tex,sha256=d45J2goEL_X-otuwtwJwXId1KHTW2sG
17
19
  in2lambda/filters/PartsOneSol/filter.py,sha256=DLj7DcDt1Aru2B4Tb5rljOFKZ4sXB8UUc4Yd3knnJow,2017
18
20
  in2lambda/filters/PartsSepSol/__init__.py,sha256=yAIUC3oJVob1R7Rrvn0sXGpK_-pXfF46DeJ9vaXSNfQ,121
19
21
  in2lambda/filters/PartsSepSol/example.tex,sha256=w7mJIcWCqKzJE_sJG4bg0J-V9kXSfe6doKWXgTd1nF8,687
20
- in2lambda/filters/PartsSepSol/filter.py,sha256=Tp2lPL2WdViSMbkIgM-VxFQce-Iuy9mQ7sXpfRu-A98,3164
21
- in2lambda/filters/Table_PartSolPartSol/__init__.py,sha256=Rf1LgSoGNkkgVN3yt6dbT_UhZs5ngMAPLpOFx2NwCBo,124
22
- in2lambda/filters/Table_PartSolPartSol/example.md,sha256=1lMw3q_O765kRroohAWnhK_8h4KMKo3Qm7_gYSxDX7A,9213
23
- in2lambda/filters/Table_PartSolPartSol/filter.py,sha256=AUGWVI4qaQBCbDSXlUgoHKA0WKgJ-7oQjvVTrZHGTEc,2260
22
+ in2lambda/filters/PartsSepSol/filter.py,sha256=9cV6HhSqOQGTk5bIS36pgBbB-5UWO3mPI2s8l9ZsNeE,3206
24
23
  in2lambda/filters/__init__.py,sha256=tla_1d85db2MAT-fgc5sNoScuYZJeTgKedzFn3NXT2c,69
25
- in2lambda/filters/markdown.py,sha256=QyZvOCQQxNOXU9_Ip8eHwEz_ljedJuLL8YLDM2OsRTA,7143
24
+ in2lambda/filters/markdown.py,sha256=6c0Q89CCtVhJ5F7of-eueUs9WqXXhMZNFq0uZr-ZxMQ,7156
26
25
  in2lambda/json_convert/__init__.py,sha256=2X3SLn6H449zXFprtZZU7M20elsOJ09mQr7EDnv04ow,87
27
- in2lambda/json_convert/json_convert.py,sha256=NckAH8SE6S5E7MkaKTVs3yLnWQ_yktMO5IquAUUVtgM,3625
28
- in2lambda/json_convert/minimal_template.json,sha256=zWsxA_9OxCtL4q6S6vj2ezqUtSYiXTSTfVc4E4xFOJ4,555
26
+ in2lambda/json_convert/json_convert.py,sha256=3C8Aa0RU1zRUXDvpvL6wV16jcGTuwVI0A1NfDcQg8fg,3662
27
+ in2lambda/json_convert/minimal_template.json,sha256=o83cZbhabXqeJiTCkLqdhijrJiiAfv1_I6HLT_QunZ8,516
29
28
  in2lambda/katex_convert/__init__.py,sha256=D0X21-NQXNrWwu_klZeCaQaqtV9vHfFv1PlrtuVAHlI,96
30
29
  in2lambda/katex_convert/delete_list.txt,sha256=Ywx8ltUbrvSxGaQVoTSXfXAe4FfPSCuRchkSCICYjHU,1753
31
30
  in2lambda/katex_convert/katex_convert.py,sha256=3UUw84432lOBnPLJwAw68MMyFPrD6ijxyYR-qVhJhnw,4909
32
31
  in2lambda/katex_convert/replace_list.txt,sha256=y5ciG4orj64eqEKsOVaQ95Zx7Xx0y4Awa4JIS27HVSw,611
33
- in2lambda/main.py,sha256=QFbLVJ-OYf8J1rkiKpMdV71NqGbnPs9E4Q5b0mXA718,5957
32
+ in2lambda/main.py,sha256=IifSQfMn1ARYlrvowYYcYLGo6b5sMOxxz-nnELk56_k,7068
34
33
  in2lambda/py.typed,sha256=gN7aphE6U0Jj-OffDD7hw1OkRs9gg6OPOrVpN5eAnCA,56
35
- in2lambda-0.3.1.dist-info/LICENSE,sha256=vEyoprnh_ic_ZQ-woDohxEIE8GJOyXqbYZ-5cH42FG4,1072
36
- in2lambda-0.3.1.dist-info/METADATA,sha256=wHlZN148j8OxJG90zKJAykhjQVjvSgOgvwsV1iKgQCc,1782
37
- in2lambda-0.3.1.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
38
- in2lambda-0.3.1.dist-info/entry_points.txt,sha256=xhkZ_lmsx7SgdhjJC-DvkFFqu-Ff-wD4Poir55bdUSM,48
39
- in2lambda-0.3.1.dist-info/RECORD,,
34
+ in2lambda-0.3.3.dist-info/LICENSE,sha256=vEyoprnh_ic_ZQ-woDohxEIE8GJOyXqbYZ-5cH42FG4,1072
35
+ in2lambda-0.3.3.dist-info/METADATA,sha256=ROh8P1bkW2CWV73U1g6zrvEhvtz5Pc_iceFsqp2e5wI,1833
36
+ in2lambda-0.3.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
37
+ in2lambda-0.3.3.dist-info/entry_points.txt,sha256=xhkZ_lmsx7SgdhjJC-DvkFFqu-Ff-wD4Poir55bdUSM,48
38
+ in2lambda-0.3.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.6.1
2
+ Generator: poetry-core 1.8.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
in2lambda/example.docx DELETED
Binary file
@@ -1,4 +0,0 @@
1
- """Filter and example TeX file for the PartSolPartSol filter.
2
-
3
- See the filter.py file for the use cases of this filter.
4
- """
@@ -1,195 +0,0 @@
1
- +-----+----------------------------------------------------------------+
2
- | Q | A Frahm\'s tachometer for measuring the rotational speed of |
3
- | 1.1 | machinery consists of many small cantilever spring steel |
4
- | | strips, each being rigidly fastened to the frame of the |
5
- | | instrument at one end and having a mass at the other (free) |
6
- | | end. A strip will vibrate (and so give a visual indication) |
7
- | | when the speed of the machine is equal to the strip\'s natural |
8
- | | frequency. One such strip is 1 mm thick, 7 mm wide and 50 mm |
9
- | | long. Calculate the mass required at the free end so that the |
10
- | | natural frequency will correspond to 1800 rev/min. |
11
- | | |
12
- | | **(0.082 kg)** |
13
- +=====+================================================================+
14
- | | Hint: Obtain the cantilever stiffness from data book. |
15
- +-----+----------------------------------------------------------------+
16
- | | |
17
- +-----+----------------------------------------------------------------+
18
- | | ![](media/image1.png){width="3.9776432633420824in" |
19
- | | height="2.075301837270341in"} |
20
- | | |
21
- | | a\) Cantilever steel strip and b) equivalent mass and spring |
22
- | | system |
23
- | | |
24
- | | Calculate the effective stiffness of the beam by considering |
25
- | | the standard formula of deflection (Engineering Data and |
26
- | | Formulae Handbook). |
27
- | | |
28
- | | ------------------------- |
29
- | | -------------------------------------------------------------- |
30
- | | -------------------------------------------------------------- |
31
- | | |
32
- | | $$\mathbf{Stiffness}\mathbf{\ }\left( \mathbf{k} \rig |
33
- | | ht)\mathbf{=}\frac{\mathbf{Force}}{\mathbf{Displacement}}$$ |
34
- | | ------------------------- |
35
- | | ------------------------------------------------------------- |
36
- | | ----------------------------------------------------- -------- |
37
- | | |
38
- | | $\delta = \frac{FL^{3}}{3EI}$ where $I = \frac{bd^{3} |
39
- | | }{12}$ (from Data and Formulae Handbook) |
40
- | | |
41
- | | $$\mathbf{\theref |
42
- | | ore}$$ $$\ \frac{F}{\delta} = k = \frac{Ebd^{3}}{4L^{3}}$$ |
43
- | | |
44
- | | ------------------------- |
45
- | | -------------------------------------------------------------- |
46
- | | -------------------------------------------------------------- |
47
- | | |
48
- | | The natural frequency is given by, |
49
- | | |
50
- | | ------------------------------------ |
51
- | | -------------------------------------------------------------- |
52
- | | $$\mathbf{\omega}_{ |
53
- | | \mathbf{n}}\mathbf{=}\sqrt{\frac{\mathbf{k}}{\mathbf{m}}}$$ |
54
- | | -------- --------------------------- |
55
- | | ----------------------------------------------------- -------- |
56
- | | |
57
- | | ------------------------------------ |
58
- | | -------------------------------------------------------------- |
59
- | | |
60
- | | so, |
61
- | | |
62
- | | ------------------------------------------------------ |
63
- | | -------------------------------------------------------------- |
64
- | | -------------------------------------------------------------- |
65
- | | $$\mathbf{\omega}_{\ |
66
- | | mathbf{n}}^{\mathbf{2}}\mathbf{=}\frac{\mathbf{Eb}\mathbf{d}^{ |
67
- | | \mathbf{3}}}{\mathbf{4}\mathbf{L}^{\mathbf{3}}\mathbf{m}}$$ |
68
- | | ------------------------- ---------------------------- |
69
- | | -------------------------------------------------------------- |
70
- | | ----------------------------------------------------- -------- |
71
- | | $$\mathbf{\therefore}$$ $$m = \frac{Edb^{3}} |
72
- | | {4L^{3}\omega_{n}^{2}}$$ |
73
- | | |
74
- | | |
75
- | | ------------------------------------------------------ |
76
- | | -------------------------------------------------------------- |
77
- | | -------------------------------------------------------------- |
78
- | | |
79
- | | Remember to convert from rpm to rad/s: ($\omega_{n}$ = |
80
- | | 188.5 rad/s) |
81
- | | |
82
- | | ------------------- |
83
- | | -------------------------------------------------------------- |
84
- | | -------------------------------------------------------------- |
85
- | | -------------------------------------------------------------- |
86
- | | -------------------------------------------------------------- |
87
- | | -------------------------------------------------------------- |
88
- | | $$\mathbf{\ |
89
- | | therefore}$$ $$\mathbf{m =}\frac{\mathbf{207 \times 1}\mathb |
90
- | | f{0}^{\mathbf{9}}\mathbf{\times 7 \times}\mathbf{10}^{\mathbf{ |
91
- | | - 3}}\mathbf{\times}\left( \mathbf{1 \times 1}\mathbf{0}^{\mat |
92
- | | hbf{- 3}} \right)^{\mathbf{3}}}{\mathbf{4 \times 0.0}\mathbf{5 |
93
- | | }^{\mathbf{3}}\mathbf{\times}\mathbf{188.5}^{\mathbf{2}}}$$ |
94
- | | ------------------- |
95
- | | ------ ------------------------------------------------------- |
96
- | | -------------------------------------------------------------- |
97
- | | -------------------------------------------------------------- |
98
- | | -------------------------------------------------------------- |
99
- | | ----------------------------------------------------- -------- |
100
- | | $$\mathbf{\ |
101
- | | therefore}$$ $$m\mathbf{=}\boxed{\mathbf{0.082\ kg}}$$ |
102
- | | |
103
- | | |
104
- | | |
105
- | | |
106
- | | |
107
- | | ------------------- |
108
- | | -------------------------------------------------------------- |
109
- | | -------------------------------------------------------------- |
110
- | | -------------------------------------------------------------- |
111
- | | -------------------------------------------------------------- |
112
- | | -------------------------------------------------------------- |
113
- +-----+----------------------------------------------------------------+
114
- | | |
115
- +-----+----------------------------------------------------------------+
116
- | Q | A circular diaphragm, fixed all round its edge, has a natural |
117
- | 1.2 | frequency of 5000 Hz. When an additional mass of 0.5 kg is |
118
- | | attached to the centre of the diaphragm, the natural frequency |
119
- | | is reduced to 4900 Hz. What are the effective mass and |
120
- | | effective stiffness of the diaphragm, both referred to the |
121
- | | central point |
122
- | | |
123
- | | **(12.14 kg,12.0 GN/m)** |
124
- +-----+----------------------------------------------------------------+
125
- | | Hint: Write the natural frequency formula twice, considering |
126
- | | the cases of with and without added mass. |
127
- +-----+----------------------------------------------------------------+
128
- | | The diaphragm has effective mass, $m$ and effective stiffness, |
129
- | | $k$ which are to be determined. The original natural frequency |
130
- | | is simply given by $\sqrt{\frac{k}{m}}$ . When a 0.5 kg mass |
131
- | | is added to the centre of the diaphragm, which is the point of |
132
- | | maximum motion, the effective mass is increased by 0.5 kg. |
133
- | | |
134
- | | ![](media/image2.png){width="2.2402777777777776in" |
135
- | | height="1.4996205161854768in"} |
136
- | | |
137
- | | > Therefore, |
138
- | | |
139
- | | ---- |
140
- | | -------------------------------------------------------------- |
141
- | | |
142
- | | $$\omega_{n1} = \sqrt{\frac{k}{m_{1}}}$$ \(1\) |
143
- | | ---- |
144
- | | ---- ------------------------------------------------ -------- |
145
- | | |
146
- | | $$\omega_{n2} = \sqrt{\frac{k}{m_{1} + 0.5}}$$ \(2\) |
147
- | | |
148
- | | ---- |
149
- | | -------------------------------------------------------------- |
150
- | | |
151
- | | The two equations are used to solve for two unknowns, |
152
- | | |
153
- | | From equ (1), |
154
- | | |
155
- | | ------------------------------------------------------------ |
156
- | | $$k = \omega_{n1}^{2}m_{1}$$ \(3\) |
157
- | | -------- ------------------------------------------ -------- |
158
- | | |
159
- | | ------------------------------------------------------------ |
160
- | | |
161
- | | Substitute equ (3) into equ (2), |
162
- | | |
163
- | | ----------------------------------------------- |
164
- | | -------------------------------------------------------------- |
165
- | | $$\omega_{n2}^{2} = \frac{\ome |
166
- | | ga_{n1}^{2}m_{1}}{m_{1} + 0.5}$$ |
167
- | | -------- -------------------------------------- |
168
- | | ----------------------------------------------------- -------- |
169
- | | $$m_{1}\left( \omega_{n1}^{2} |
170
- | | - \omega_{n2}^{2} \right) + \frac{\omega_{n2}^{2}}{2} = 0$$ |
171
- | | |
172
- | | ----------------------------------------------- |
173
- | | -------------------------------------------------------------- |
174
- | | |
175
- | | > Remember to convert Hz to rad/s. |
176
- | | |
177
- | | +-------+-----------------------------------------+-------+ |
178
- | | | | $$m_{1} | | |
179
- | | | | = \frac{\omega_{n2}^{2}}{2\left( \omeg | | |
180
- | | | | a_{n1}^{2} - \omega_{n2}^{2} \right)}$$ | | |
181
- | | | | | | |
182
- | | | | $$\mat | | |
183
- | | | | hbf{\ \ \ \ \ \ \ =}\frac{(4900 \times | | |
184
- | | | | 2\pi)^{2}}{2\left( (5000 \times 2\pi)^ | | |
185
- | | | | {2} - (4900 \times 2\pi)^{2} \right)}$$ | | |
186
- | | | | | | |
187
- | | | | $$\mathbf{\ \ \ | | |
188
- | | | | \ \ \ \ =}\boxed{\mathbf{12.13\ kg}}$$ | | |
189
- | | +=======+=========================================+=======+ |
190
- | | +-------+-----------------------------------------+-------+ |
191
- | | |
192
- | | > And from equ (3), $k = \boxed{\mathbf{12.0\ GN/m}}$ |
193
- +-----+----------------------------------------------------------------+
194
- | | |
195
- +-----+----------------------------------------------------------------+
@@ -1,61 +0,0 @@
1
- """A solution appears after each individual part."""
2
-
3
- from collections import deque
4
- from typing import Optional
5
-
6
- import panflute as pf
7
-
8
- from in2lambda.api.module import Module
9
- from in2lambda.filters.markdown import filter
10
-
11
-
12
- @filter
13
- def pandoc_filter(
14
- elem: pf.Element,
15
- doc: pf.elements.Doc,
16
- module: Module,
17
- parsing_answers: bool,
18
- ) -> Optional[pf.Str]:
19
- """A Pandoc filter that parses and translates various TeX elements.
20
-
21
- Args:
22
- elem: The current TeX element being processed. This could be a paragraph,
23
- ordered list, etc.
24
- doc: A Pandoc document container - essentially the Pandoc AST.
25
- module: The Python API that is used to store the result after processing
26
- the TeX file.
27
- parsing_answers: Whether an answers-only document is currently being parsed.
28
-
29
- Returns:
30
- Converted TeX elements for the AST where required
31
- e.g. replaces math equations so that they are surrounded by $.
32
- """
33
- # Question text (ListItem -> List -> Doc)
34
- if isinstance(elem, pf.Table):
35
- match type(elem):
36
- case pf.Para:
37
- pandoc_filter.solutions = deque()
38
- if hasattr(pandoc_filter, "question"):
39
- pandoc_filter.question.append(pf.stringify(elem))
40
- else:
41
- pandoc_filter.question = [pf.stringify(elem)]
42
- case pf.OrderedList:
43
- for listItem in elem.content:
44
- part = [
45
- pf.stringify(item)
46
- for item in listItem.content
47
- if not isinstance(item, pf.Div)
48
- ]
49
- module.current_question.add_part_text("\n".join(part))
50
- module.current_question.add_solution(
51
- pandoc_filter.solutions.popleft()
52
- )
53
-
54
- if isinstance(elem, pf.Div):
55
- pandoc_filter.solutions.append(pf.stringify(elem))
56
- if pandoc_filter.question:
57
- module.add_question(main_text="\n".join(pandoc_filter.question))
58
- module.current_question.add_solution(pf.stringify(elem))
59
- module.current_question._last_part["solution"] -= 1
60
- pandoc_filter.question = []
61
- return None