Closed MHajoha closed 5 months ago
Die Generic-Ketten zu entschärfen, finde ich gut. So sieht es schon einfacher aus.
Wie wäre es denn, wenn wir schreiben
class ExampleQuestion(Question):
attempt_class = ExampleAttempt
options: MyModel
state: MyState # Angabe nur notwendig, wenn man von BaseQuestionState abweichen will.
und man im Gegenzug in QuestionType
die options_class
nicht angeben muss? Hier wäre der Vorteil, dass die Typen von options (und state) Typecheckern und IDE explizit bekannt gemacht werden.
Bezüglich _merge_uis
: Hier stellt sich mir die Frage, ob wir nicht einfach in der QPPE vorsehen sollten, dass die Bereiche getrennt übermittelt werden sollen (statt sie zu einem XML zusammenzuführen). Bei der Gelegenheit sollten wir auch include_inline_css
entfernen, denke ich.
Idealerweise schaffen wir es, {{ question.state.abc }} automatisch in eine PI umzuwandeln und placeholders auch aus der Template zu generieren. Ich werde mal schauen, was für Möglichkeiten Jinja da bietet.
Das wäre schon interessant, um schnell gemachte Fehler zu vermeiden, die zu XSS-Lücken führen. Wobei durch das autoescape Feature von jinja die Gefahr schon ziemlich reduziert wird, oder? Bei unseren Placeholders geht es ja darum, dass wir User-Eingaben durch das LMS ungefiltert oder gefiltert an den Browser weiterleiten wollen, ohne aber dass diese Eingaben als QPy-XML interpretiert werden. Das heißt wir könnten hier auch einen Funktionsaufruf/Filter verlangen und es gäbe keine schlimmen Folgen, wenn das mal vergessen wird, solange autoescape aktiviert ist und die Devs nicht | safe
schreiben.
Die Generic-Ketten zu entschärfen, finde ich gut. So sieht es schon einfacher aus.
Wie wäre es denn, wenn wir schreiben
class ExampleQuestion(Question): attempt_class = ExampleAttempt options: MyModel state: MyState # Angabe nur notwendig, wenn man von BaseQuestionState abweichen will.
und man im Gegenzug in
QuestionType
dieoptions_class
nicht angeben muss? Hier wäre der Vorteil, dass die Typen von options (und state) Typecheckern und IDE explizit bekannt gemacht werden.
Die Idee gefällt mir, ich habe es mal testweise in https://github.com/questionpy-org/questionpy-sdk/pull/77/commits/642d1b5f8b55034659836fa999bf0120e692435f umgesetzt. Beim scoring state ist es leider nicht mehr ganz so simpel, weil die Annotation MeinScoringState | None
sein muss. Ich verwende dort jetzt immer den Teil der Union, der eine Subklasse von BaseScoringState
ist
get_type_arg
auf die Generic-Parameter zugegriffen, um anQuestion
-,Attempt
,*State
-Implementierung etc. zu kommen. Das führte aber leider zu schwer nachzuvollziehenden Klassenköpfen. (insb. weil Typenparameter nicht per Keyword angegeben werden können, und auch erst seit 3.11 defaults haben) Stattdessen werden jetzt direkt Klassenvariablen erwartet.AttemptUi
jetzt in getrennten Methodenrender_formulation
,render_general_feedback
, etc. gebaut, und dann von der Default-Impl vonrender_ui
bzw.render_subquestion
zusammengesetzt. Außerdem wird ein Jinja-Environment bereitgestellt, das die Templates aller geladenen Pakete und der SDK auffindbar macht und qtype, question, attempt und environment als globals hinzufügt. Ein paar offene Probleme bleiben da noch:<?p
-PIs schreiben undplaceholders
up-to-date halten müssen. Idealerweise schaffen wir es,{{ question.state.abc }}
automatisch in eine PI umzuwandeln undplaceholders
auch aus der Template zu generieren. Ich werde mal schauen, was für Möglichkeiten Jinja da bietet.