Merge branch 'main' into feature/table
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
Prüfungsfragen zum Erwerb von Amateurfunkprüfungsbescheinigungen
|
||||||
|
3. Auflage, März 2024
|
||||||
|
|
||||||
|
Hinweis:
|
||||||
|
========
|
||||||
|
|
||||||
|
Die Prüfungsfragen werden in maschinenlesbarer Form von der
|
||||||
|
Bundesnetzagentur für Elektrizität, Gas, Telekommunikation, Post und Eisenbahnen,
|
||||||
|
Tulpenfeld 4, 53113 Bonn
|
||||||
|
unter der Datenlizenz DL-DE->BY-2.0 bereitgestellt.
|
||||||
|
|
||||||
|
Die bereitgestellten Daten und Metadaten dürfen für die kommerzielle und nicht kommerzielle Nutzung insbesondere
|
||||||
|
vervielfältigt, ausgedruckt, präsentiert, verändert, bearbeitet sowie an Dritte übermittelt werden;
|
||||||
|
mit eigenen Daten und Daten Anderer zusammengeführt und zu selbständigen neuen Datensätzen verbunden werden;
|
||||||
|
in interne und externe Geschäftsprozesse, Produkte und Anwendungen in öffentlichen und nicht öffentlichen
|
||||||
|
elektronischen Netzwerken eingebunden werden.
|
||||||
|
|
||||||
|
Bei der Nutzung ist sicherzustellen, dass folgende Angaben als Quellenvermerk enthalten sind:
|
||||||
|
|
||||||
|
"Prüfungsfragen zum Erwerb von Amateurfunkprüfungsbescheinigungen, Bundesnetzagentur, 3. Auflage, März 2024,
|
||||||
|
(www.bundesnetzagentur.de/amateurfunk), Datenlizenz Deutschland – Namensnennung – Version 2.0
|
||||||
|
(www.govdata.de/dl-de/by-2-0)"
|
||||||
|
|
||||||
|
Veränderungen, Bearbeitungen, neue Gestaltungen oder sonstige Abwandlungen sind im Quellenvermerk mit dem Hinweis
|
||||||
|
zu versehen, dass die Daten geändert wurden.
|
||||||
|
|
||||||
|
|
||||||
|
Anleitung:
|
||||||
|
==========
|
||||||
|
|
||||||
|
Die maschinenlesbare Version der Prüfungsfragen teilt sich in zwei Teile auf:
|
||||||
|
|
||||||
|
1. Die Struktur des Katalogs und die Prüfungsfragen als JSON-Datei
|
||||||
|
2. Alle Bilder zu den Prüfungsfragen als SVG-Dateien
|
||||||
|
|
||||||
|
Das JSON-Schema besteht aus Kapiteln "sections", die jeweils einen Titel
|
||||||
|
"title" beinhalten. Eine Section kann entweder weitere Unterkapitel "sections"
|
||||||
|
beinhalten, oder eine Liste von Prüfungsfragen "questions". Eine Prüfungsfrage
|
||||||
|
beinhaltet folgende Attribute:
|
||||||
|
|
||||||
|
- "number" : Die Prüfungsnummer im Fragenkatalog
|
||||||
|
- "class" : Die Klasse (1:N, 2:E, 3:A)
|
||||||
|
- "question" : Der Fragenkopf
|
||||||
|
- "answer_a" : Die richtige Antwort A
|
||||||
|
- "answer_b" : Ein Distraktor B (falsche Antwort)
|
||||||
|
- "answer_c" : Ein Distraktor C (falsche Antwort)
|
||||||
|
- "answer_d" : Ein Distraktor D (falsche Antwort)
|
||||||
|
- "picture_question" : Das Fragenbild [Optional]
|
||||||
|
- "picture_a" : Das Antwortbild A [Optional]
|
||||||
|
- "picture_b" : Das Antwortbild B [Optional]
|
||||||
|
- "picture_c" : Das Antwortbild C [Optional]
|
||||||
|
- "picture_d" : Das Antwortbild D [Optional]
|
||||||
|
|
||||||
|
Die Prüfungsfragen enthalten LaTeX-Code für die Darstellung der Formeln. Diese
|
||||||
|
können durch verschiedene Bibliotheken z. B. durch KaTeX (https://katex.org)
|
||||||
|
angezeigt werden.
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
+15752
File diff suppressed because it is too large
Load Diff
@@ -115,7 +115,7 @@ class FiftyOhmLaTeXRenderer(LaTeXRenderer):
|
|||||||
\label{{{ref}}}"""
|
\label{{{ref}}}"""
|
||||||
|
|
||||||
def render_photo(self, token) :
|
def render_photo(self, token) :
|
||||||
return self.render_picture_helper(token.id, token.ref, token.text, token.number)
|
return self.render_photo_helper(token.id, token.ref, token.text, token.number)
|
||||||
|
|
||||||
def render_table(self, token):
|
def render_table(self, token):
|
||||||
alignments = token.alignment
|
alignments = token.alignment
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ class Photo(SpanToken):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
parse_inner = False
|
parse_inner = False
|
||||||
pattern = re.compile(r"\[photo:(\d+):(.+):(.+)\]")
|
pattern = re.compile(r"\[photo:(\d+):([^:\]]+):([^:\]]+)\]")
|
||||||
|
|
||||||
def __init__(self, match_object):
|
def __init__(self, match_object):
|
||||||
self.id = match_object.group(1)
|
self.id = match_object.group(1)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<div class="question layout-{{ layout }}" id="{{ number }}">
|
||||||
|
<div class="question-text">{{ number }}: {{ question }}</div>
|
||||||
|
{% if picture_question %}
|
||||||
|
<div class="question-picture"><img src="img/picture_{{ picture_question }}.svg"></div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="answers">
|
||||||
|
{% for i in range(4) %}
|
||||||
|
<button onclick="answer(this, {{ 'true' if i == 0 else 'false' }} );">
|
||||||
|
{% if answers[i] %}
|
||||||
|
{{ answers[i] }}
|
||||||
|
{% endif %}
|
||||||
|
{% if answer_pictures[i] %}
|
||||||
|
<div class="question-picture"><img src="img/picture_{{ answer_pictures[i] }}.svg"></div>
|
||||||
|
{% endif %}
|
||||||
|
{% if i == 0 %}
|
||||||
|
<div class="result material-icons">check</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="result material-icons">clear</div>
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
+69
-3
@@ -1,11 +1,76 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from mistletoe import Document
|
from mistletoe import Document
|
||||||
|
|
||||||
from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer
|
from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer
|
||||||
|
|
||||||
|
|
||||||
def question_stub(input):
|
def parse_katalog():
|
||||||
return f"<question>{input}</question>" # TODO implement this stub
|
with open('data/fragenkatalog3b.json') as fragenkatalog_file:
|
||||||
|
fragenkatalog = json.load(fragenkatalog_file)
|
||||||
|
|
||||||
|
questions = {}
|
||||||
|
|
||||||
|
for exampart in fragenkatalog["sections"] :
|
||||||
|
for chapter in exampart["sections"] :
|
||||||
|
if "questions" in chapter :
|
||||||
|
for question in chapter["questions"] :
|
||||||
|
questions[question["number"]] = question
|
||||||
|
if "sections" in chapter :
|
||||||
|
for section in chapter["sections"] :
|
||||||
|
for question in section["questions"] :
|
||||||
|
questions[question["number"]] = question
|
||||||
|
|
||||||
|
return questions
|
||||||
|
|
||||||
|
env = Environment(loader=FileSystemLoader('templates/html'))
|
||||||
|
question_template = env.get_template('question.html')
|
||||||
|
questions = parse_katalog()
|
||||||
|
|
||||||
|
def question_stub(input):
|
||||||
|
""" Combines the original question dataset from BNetzA with our internal metadata """
|
||||||
|
with open('data/metadata.json') as metadata_file :
|
||||||
|
metadata = json.load(metadata_file)
|
||||||
|
number = metadata[f"{input}"]["number"] # Fragennummer z.B. AB123
|
||||||
|
|
||||||
|
question = questions[number]
|
||||||
|
metadata = metadata[f"{input}"]
|
||||||
|
|
||||||
|
if "answer_a" in question :
|
||||||
|
answers = [
|
||||||
|
question["answer_a"],
|
||||||
|
question["answer_b"],
|
||||||
|
question["answer_c"],
|
||||||
|
question["answer_d"]
|
||||||
|
]
|
||||||
|
else :
|
||||||
|
answers = []
|
||||||
|
|
||||||
|
if metadata["picture_a"] != "" :
|
||||||
|
answer_pictures = [
|
||||||
|
metadata["picture_a"],
|
||||||
|
metadata["picture_b"],
|
||||||
|
metadata["picture_c"],
|
||||||
|
metadata["picture_d"]
|
||||||
|
]
|
||||||
|
else :
|
||||||
|
answer_pictures = []
|
||||||
|
|
||||||
|
if "picture_question" in question :
|
||||||
|
picture_question = metadata["picture_question"]
|
||||||
|
else :
|
||||||
|
picture_question = ""
|
||||||
|
|
||||||
|
return question_template.render(
|
||||||
|
question=question["question"],
|
||||||
|
number=number,
|
||||||
|
layout=metadata["layout"],
|
||||||
|
picture_question=picture_question,
|
||||||
|
answers=answers,
|
||||||
|
answer_pictures=answer_pictures
|
||||||
|
)
|
||||||
|
|
||||||
def test_html(capsys):
|
def test_html(capsys):
|
||||||
with capsys.disabled():
|
with capsys.disabled():
|
||||||
@@ -14,4 +79,5 @@ def test_html(capsys):
|
|||||||
with FiftyOhmHtmlRenderer(question_stub) as renderer:
|
with FiftyOhmHtmlRenderer(question_stub) as renderer:
|
||||||
output = renderer.render(Document(content))
|
output = renderer.render(Document(content))
|
||||||
with open("test/acceptanceTest.html", "w") as output_file:
|
with open("test/acceptanceTest.html", "w") as output_file:
|
||||||
output_file.write(output)
|
pretty = BeautifulSoup(output, 'html.parser').prettify()
|
||||||
|
output_file.write(pretty)
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import mistletoe
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer
|
||||||
|
from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer
|
||||||
|
from test.util import paragraph
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.html
|
||||||
|
def test_photo_html():
|
||||||
|
assertions = {
|
||||||
|
"[photo:123:abc:Text]": FiftyOhmHtmlRenderer.render_photo_helper("123", "abc", "Text", "TODO"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for assertion in assertions:
|
||||||
|
assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == paragraph(assertions[assertion])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.latex
|
||||||
|
def test_photo_latex():
|
||||||
|
assertions = {
|
||||||
|
"[photo:123:abc:Text]": FiftyOhmLaTeXRenderer.render_photo_helper("123", "abc", "Text", "TODO"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for assertion in assertions:
|
||||||
|
assert mistletoe.markdown(assertion, FiftyOhmLaTeXRenderer) == "\n" + assertions[assertion] + "\n"
|
||||||
+25
-5
@@ -1,3 +1,5 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from mistletoe import Document
|
from mistletoe import Document
|
||||||
|
|
||||||
@@ -22,18 +24,36 @@ def test_question_html():
|
|||||||
for assertion in assertions:
|
for assertion in assertions:
|
||||||
assert renderer.render(Document(assertion)) == assertions[assertion]
|
assert renderer.render(Document(assertion)) == assertions[assertion]
|
||||||
|
|
||||||
|
@pytest.mark.html
|
||||||
|
def test_question_translation_html():
|
||||||
|
assertions = {
|
||||||
|
"[question:123]": "ED111\n",
|
||||||
|
"\n[question:123]\n": "ED111\n",
|
||||||
|
"\n[question:123]\n[question:123]": "ED111\nED111\n",
|
||||||
|
"Foo\n[question:123]\nBar": paragraph("Foo") + "ED111\n" + paragraph("Bar"),
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_function(input):
|
||||||
|
with open('data/metadata.json') as file:
|
||||||
|
metadata = json.load(file)
|
||||||
|
return metadata[f"{input}"].get("number")
|
||||||
|
|
||||||
|
with FiftyOhmHtmlRenderer(test_function) as renderer:
|
||||||
|
for assertion in assertions:
|
||||||
|
assert renderer.render(Document(assertion)) == assertions[assertion]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.latex
|
@pytest.mark.latex
|
||||||
def test_question_latex():
|
def test_question_latex():
|
||||||
assertions = {
|
assertions = {
|
||||||
"[question:123]": "123",
|
"[question:123]": "\question{123}",
|
||||||
"\n[question:123]\n": "123",
|
"\n[question:123]\n": "\question{123}",
|
||||||
"\n[question:123]\n[question:123]": "123123",
|
"\n[question:123]\n[question:123]": "\question{123}\question{123}",
|
||||||
"Foo\n[question:123]\nBar": "\nFoo\n123\nBar\n",
|
"Foo\n[question:123]\nBar": "\nFoo\n\question{123}\nBar\n",
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_function(input):
|
def test_function(input):
|
||||||
return f"{input}"
|
return "\\question{"+input+"}"
|
||||||
|
|
||||||
with FiftyOhmLaTeXRenderer(test_function) as renderer:
|
with FiftyOhmLaTeXRenderer(test_function) as renderer:
|
||||||
for assertion in assertions:
|
for assertion in assertions:
|
||||||
|
|||||||
Reference in New Issue
Block a user