diff --git a/assets/latex.js b/assets/latex.js index bdf64c5..1181000 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -1,69 +1,158 @@ document.addEventListener("DOMContentLoaded", function() { - var macros = { - ",": (context) => context.future()?.text === " " ? "{\\char`,}" : "\\char`,", - "\\qty": "{#1\\,\\mathrm{#2}}", - "\\qtyrange": "{#1\\text{--}#2\\,\\mathrm{#3}}", - "\\unit": "{\\mathrm{#1}}", - "\\num": "{#1}", - "\\squared": "{^{2}}", - "\\cubed": "{^{3}}", - "\\per": "/", - "\\percent": "\\%", - "\\tera": "\\text{T}", - "\\giga": "\\text{G}", - "\\mega": "\\text{M}", - "\\kilo": "\\text{k}", - "\\dezi": "\\text{d}", - "\\centi": "\\text{c}", - "\\milli": "\\text{m}", - "\\micro": "\\text{μ}", - "\\nano": "\\text{n}", - "\\pico": "\\text{p}", - "\\kilogram": "\\text{kg}", - "\\gram": "\\text{g}", - "\\meter": "\\text{m}", - "\\second": "\\text{s}", - "\\hour": "\\text{h}", - "\\ampere": "\\text{A}", - "\\kelvin": "\\text{K}", - "\\mol": "\\text{mol}", - "\\bel": "\\text{B}", - "\\dezibel": "\\text{dB}", - "\\dB": "\\text{dB}", - "\\dBm": "\\text{dBm}", - "\\dBu": "\\text{dBu}", - "\\dBW": "\\text{dBW}", - "\\dBi": "\\text{dBi}", - "\\dBd": "\\text{dBd}", - "\\candela": "\\text{cd}", - "\\newton": "\\text{N}", - "\\hertz": "\\text{Hz}", - "\\pascal": "\\text{Pa}", - "\\volt": "\\text{V}", - "\\watt": "\\text{W}", - "\\joule": "\\text{J}", - "\\henry": "\\text{H}", - "\\farad": "\\text{F}", - "\\coulomb": "\\text{C}", - "\\ohm": "\\Omega", - "\\Ohm": "\\Omega", - "\\weber": "\\text{Wb}", - "\\tesla": "\\text{T}", - "\\bit": "\\text{b}", - "\\baud": "\\text{Bd}", - "\\degree": "^\\circ", - "\\degreeCelsius": "^\\circ\\text{C}", - }; + function formatThousands(input) { + + let s = String(input).trim(); + + // Extract exponent (e or E) and convert to \cdot 10^{...} + let exponent = ""; + const expMatch = s.match(/([eE])([+-]?\d+)$/); + if (expMatch) { + let expValue = expMatch[2]; + + // Remove leading "+" + if (expValue[0] === "+") { + expValue = expValue.slice(1); + } + + // Parse exponent as a numeric value + const expNum = parseInt(expValue, 10); + + // Suppress exponent if it is 0 (no \cdot 10^0 shown) + if (expNum !== 0) { + exponent = "\\cdot 10^{" + expNum + "}"; + } + + s = s.slice(0, -expMatch[0].length); + } + + // Sign (+, -, \pm). Also accept "\\pm" (double-escaped backslash) and optional whitespace. + let sign = ""; + const pmMatch = s.match(/^(?:\\\\pm|\\pm)\s*/); + if (pmMatch) { + sign = "\\pm"; + s = s.slice(pmMatch[0].length); + } else if (s[0] === "+" || s[0] === "-") { + sign = s[0]; + s = s.slice(1); + } + + // Find decimal point (last . or ,) + let intPart = s; + let fracSep = ""; + let fracDigits = ""; + const decMatch = s.match(/([.,])(\d+)$/); + if (decMatch) { + intPart = s.slice(0, decMatch.index); + fracSep = decMatch[1]; + fracDigits = decMatch[2]; + } + + // Thousand grouping in integer part + if (intPart.length >= 5) { + intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, "\\,"); + } + + // Thousand grouping in fractional part (from left in 3-digit blocks) + if (fracDigits.length >= 4) { + fracDigits = fracDigits.replace(/(\d{3})(?=\d)/g, "$1\\,"); + } + + // Reassemble + return sign + intPart + (fracDigits ? (fracSep + fracDigits) : "") + exponent; + } + + + var macros = { + ",": (context) => context.future()?.text === " " ? "{\\char`,}" : "\\char`,", + "\\num": (context) => { + const [arg] = context.consumeArgs(1); + const raw = arg.reverse().map(t => t.text).join(""); + return formatThousands(raw); + }, + "\\qty": (context) => { + const [numTok, unitTok] = context.consumeArgs(2); + const rawNum = numTok.reverse().map(t => t.text).join(""); + const unit = unitTok.reverse().map(t => t.text).join(""); + const formattedNum = formatThousands(rawNum); + if (unit.startsWith("\\degree")) { + return formattedNum + "\\mathrm{" + unit + "}"; + } else { + return formattedNum + "\\,\\mathrm{" + unit + "}"; + } + }, + "\\qtyrange": (context) => { + const [aTok, bTok, unitTok] = context.consumeArgs(3); + const aRaw = aTok.reverse().map(t => t.text).join(""); + const bRaw = bTok.reverse().map(t => t.text).join(""); + const unit = unitTok.reverse().map(t => t.text).join(""); + const aFmt = formatThousands(aRaw); + const bFmt = formatThousands(bRaw); + if (unit.startsWith("\\degree")) { + return aFmt + "\\text{--}\\," + bFmt + "\\mathrm{" + unit + "}"; + } else { + return aFmt + "\\,\\text{--}\\," + bFmt + "\\,\\mathrm{" + unit + "}"; + } + }, + "\\unit": "{\\mathrm{#1}}", + "\\squared": "{^{2}}", + "\\cubed": "{^{3}}", + "\\per": "/", + "\\percent": "\\%", + "\\tera": "\\text{T}", + "\\giga": "\\text{G}", + "\\mega": "\\text{M}", + "\\kilo": "\\text{k}", + "\\dezi": "\\text{d}", + "\\centi": "\\text{c}", + "\\milli": "\\text{m}", + "\\micro": "\\text{μ}", + "\\nano": "\\text{n}", + "\\pico": "\\text{p}", + "\\kilogram": "\\text{kg}", + "\\gram": "\\text{g}", + "\\meter": "\\text{m}", + "\\second": "\\text{s}", + "\\hour": "\\text{h}", + "\\ampere": "\\text{A}", + "\\kelvin": "\\text{K}", + "\\mol": "\\text{mol}", + "\\bel": "\\text{B}", + "\\dezibel": "\\text{dB}", + "\\dB": "\\text{dB}", + "\\dBm": "\\text{dBm}", + "\\dBu": "\\text{dBu}", + "\\dBW": "\\text{dBW}", + "\\dBi": "\\text{dBi}", + "\\dBd": "\\text{dBd}", + "\\candela": "\\text{cd}", + "\\newton": "\\text{N}", + "\\hertz": "\\text{Hz}", + "\\pascal": "\\text{Pa}", + "\\volt": "\\text{V}", + "\\watt": "\\text{W}", + "\\joule": "\\text{J}", + "\\henry": "\\text{H}", + "\\farad": "\\text{F}", + "\\coulomb": "\\text{C}", + "\\ohm": "\\Omega", + "\\Ohm": "\\Omega", + "\\weber": "\\text{Wb}", + "\\tesla": "\\text{T}", + "\\bit": "\\text{b}", + "\\baud": "\\text{Bd}", + "\\degreeCelsius": "\\,\\degree\\text{C}", + }; + + renderMathInElement(document.body, { + delimiters: [ + { left: '$$', right: '$$', display: true }, // Note: $$ has to come before $ + { left: '$', right: '$', display: false }, + { left: '\\[', right: '\\]', display: true }, + { left: "\\(", right: "\\)", display: false }, + ], + throwOnError: false, + macros: macros + }); - renderMathInElement(document.body, { - delimiters: [ - {left: '$$', right: '$$', display: true}, // Note: $$ has to come before $ - {left: '$', right: '$', display: false}, - {left: '\\[', right: '\\]', display: true}, - {left: "\\(", right: "\\)", display: false}, - ], - throwOnError : false, - macros: macros - }); }); \ No newline at end of file diff --git a/assets/style-course.css b/assets/style-course.css index 846f5e4..03b1830 100644 --- a/assets/style-course.css +++ b/assets/style-course.css @@ -19,11 +19,12 @@ font-style: italic; } -.question { +.question:not(.for-solution) { padding: 1ex; background: #ddd; margin-top: 2em; margin-bottom: 2em; + position: relative; } .question p { padding: 0; @@ -34,6 +35,20 @@ font-weight: 600; margin: 1ex 1em 1.5ex 1em; } + +.solution { + margin-top: -1.5em; + margin-bottom: 1em; + text-align: end; +} + +.solution .solution-icon { + width: 1em; + height: 1em; + display: block; + margin-right: 0.35em; +} + .question .question-picture { max-width: 100%; margin-bottom: 1em; @@ -171,7 +186,7 @@ td { } ul { - list-style-type: square; + list-style-type: square; } ul li::marker { @@ -419,15 +434,15 @@ span.formula { /* Enable Line Breaks in Equations ... */ .course .katex-display > .katex { - white-space: normal + white-space: normal; } /* Add space between broken lines: */ .course .katex-display .base { - margin: 0.5em 0em + margin: 0.5em 0em; } /* Compensate by reducing space around display math */ .course .katex-display { - margin: 1em 0em; -} \ No newline at end of file + margin: 1em 0em; +} diff --git a/build.py b/build.py index b913c57..5caae9f 100644 --- a/build.py +++ b/build.py @@ -13,3 +13,4 @@ bd.build_edition("NE") bd.build_edition("EA") bd.build_edition("NEA") bd.build_assets() +bd.build_solutions() diff --git a/src/build.py b/src/build.py index d9ebd14..cb70b55 100644 --- a/src/build.py +++ b/src/build.py @@ -105,6 +105,8 @@ class Build: picture_question = "" alt_text_question = "" + solution_file = self.config.p_data_solutions / f"{number}.md" + return question_template.render( question=question["question"], number=number, @@ -114,6 +116,7 @@ class Build: answer_pictures=answer_pictures, alt_text_answers=alt_text_answers, alt_text_question=alt_text_question, + has_solution=solution_file.exists(), ) def __build_question_slide(self, input): @@ -474,3 +477,23 @@ class Build: self.__build_course_page(snippets, "patenkarte", "patenkarte") self.__build_html_page(contents, "pruefung") self.__build_html_page(contents, "infos") + + def build_solutions(self): + for solution_file in self.config.p_data_solutions.glob("*.md"): + with solution_file.open() as file: + content = file.read() + with (self.config.p_build / f"{solution_file.stem}.html").open("w") as file: + with FiftyOhmHtmlRenderer( + question_renderer=self.__build_question, + picture_handler=self.__picture_handler, + photo_handler=self.__photo_handler, + include_handler=self.__include_handler, + ) as renderer: + question = self.__build_question( + solution_file.stem, template_file="html/solution_question.html" + ) + solution_template = self.env.get_template("html/solution.html") + solution = renderer.render(Document(content)) + page = solution_template.render(question=question, solution=solution, number=solution_file.stem) + page = self.__build_page(page, course_wrapper=False) + file.write(page) diff --git a/src/config.py b/src/config.py index 2fdc66d..dc86552 100644 --- a/src/config.py +++ b/src/config.py @@ -26,6 +26,7 @@ class Config: self.p_data_slides = self.p_data_contents / "slides" self.p_data_snippets = self.p_data_contents / "snippets" self.p_data_static = self.p_data_contents / "static" + self.p_data_solutions = self.p_data_contents / "solutions" self.p_data_fragenkatalog = self.p_data_questions / "fragenkatalog3b.json" self.p_data_metadata = self.p_data_questions / "metadata3b.json" diff --git a/templates/html/question.html b/templates/html/question.html index a95851c..bb613b8 100644 --- a/templates/html/question.html +++ b/templates/html/question.html @@ -1,25 +1,37 @@
-
{{ number }}: {{ question }}
- {% if picture_question %} -
{{ alt_text_question }}
- {% endif %} -
- {% for i in range(4) %} - - {% endfor %} -
+
+ {{ number }}: {{ question }}
- + {% if picture_question %} +
{{ alt_text_question }}
+ {% endif %} +
+ {% for i in range(4) %} + + {% endfor %} +
+
+
+ {% if has_solution %} + + + Lösungsweg anzeigen + + {% endif %} +
+ \ No newline at end of file diff --git a/templates/html/solution.html b/templates/html/solution.html new file mode 100644 index 0000000..aecae60 --- /dev/null +++ b/templates/html/solution.html @@ -0,0 +1,16 @@ + diff --git a/templates/html/solution_question.html b/templates/html/solution_question.html new file mode 100644 index 0000000..409d67c --- /dev/null +++ b/templates/html/solution_question.html @@ -0,0 +1,21 @@ +
+
Frage: {{ question }}
+ {% if picture_question %} +
{{ alt_text_question }}
+ {% endif %} +
+ {% for i in range(1) %} +
+ {{ 'Lösung:' if i == 0 else 'Falsch:' }} + {% if answers[i] %} + {{ answers[i] }} + {% endif %} + {% if answer_pictures[i] %} +
{{ alt_text_answers[i] }}
+ {% endif %} +
+ {% endfor %} +
+
+ + \ No newline at end of file