WeBWorKのコードリーディング
WeBWorKのコードリーディング
疑問点
どうやって解答を判定しているの?
どうやって独自記法から数式画像を出力しているの?
軽く読む
原理が気になる部分
attemptResults
attemptResults
解答をジャッジシステムに投げた結果?
attemptResultという関数は、実際にはHTML::AttemptsTableのコンストラクタを呼び出しているだけである
code:attemptResults.pm
HTML::AttemptsTable
code:formatAnswerRow.pm
my $answerString = $rh_answer->{student_ans} // '';
my $answerPreview = $self->previewAnswer($rh_answer) // ' ';
my $correctAnswer = $rh_answer->{correct_ans} // '';
my $correctAnswerPreview = $self->previewCorrectAnswer($rh_answer) // ' ';
my $answerMessage = $rh_answer->{ans_message} // '';
$answerMessage =~ s/\n/<BR>/g;
my $answerScore = $rh_answer->{score} // 0;
rh_answer
既にジャッジを終えたあとの結果が格納されていると読める
previewCorrectAnswerにrh_answerを渡すと、正しい答えの数式画像が受け取れる?
previewAnswer
引数は
code:previewAnswer.pm
sub previewAnswer ($self, $answerResult) {
my $displayMode = $self->displayMode;
my $imgGen = $self->imgGen;
my $tex = $answerResult->{preview_latex_string};
return '' unless defined $tex and $tex ne '';
return $tex if $answerResult->{non_tex_preview};
if ($displayMode eq 'plainText') {
return $tex;
} elsif (($answerResult->{type} // '') eq 'essay') {
return $tex;
} elsif ($displayMode eq 'images') {
return $imgGen->add($tex);
} elsif ($displayMode eq 'MathJax') {
return $self->c->tag('script', type => 'math/tex; mode=display', $self->c->b($tex));
}
}
初期化時に設定したdisplayModeにしたがって、答えのプレビューを生成する
plainTextやessayの場合は考えない
attemptResultsは、既にどこかから答えのtex文字列を受け取っている attemptResultsは、outputSummaryから呼ばれている
outputSummaryは、Problemページのテンプレートから呼ばれている
Problemページのテンプレートに誰が答えを渡すのか?
エントリポイントを探す
Webページをルーティングしている存在
docker-entrypoint.sh
Minionにbin/webwork2を渡す
bin/webwork2
このファイルを出発点に見ていく
lib/Mojolicious/WeBWorK.pm
ルーティング
その他ルーティング関連
「GatewayQuiz」テンプレートにとてもたくさん書かれていた
答案の表示
pg_resultsがどこから渡されたのか?
フォーム部分を読みたい
pg_resultsのセット
Problemの処理
submitAnswersパラメータ
Submit Answersという文字列になっていた
create_ans_str_from_responses
templateNameが気になるt6o_o6t.icon
systemという値になっている
何か見つけられないか?
読む対象の挙動をよく理解していること!!
脆弱性を見つけるのとは違って、原理を知りたいから、挙動を把握しているほど読みやすい
https://scrapbox.io/files/6526a87d33ec4c001bd87c46.png
提出前画面と、提出後画面は同じソースコードで出来ている
提出フォームの提出ボタンには、「Submit Answers」というvalueが割り当てられている
「提出(submitAnswers)」または「プレビュー(previewAnswers)」という状態を表している
バックエンドは、リクエストのsubmitAnswersを見て、現在のユーザ画面が提出直後かを判定する
提出直後なら、提出結果を処理する必要がある
フォームの値
pg = ProbremGrader
GatewayQuizだけでは説明できない
例えば、回答欄を示すinput要素が存在しない
実際にサーバーから返ってくる要素にはある
GatewayQuizでuseされている、SingleProblemGraderが関連している?
状態がsubmitAnswersならば、resultsTableにattemptResultsの戻り値を入れる
resultsTableの代入後はそのまま埋め込み表示する
GatewayQuizではないかもしれない
「You have attempt」というテキストは、Problem
/icons/hr.icon
Results for this submissionを検索するとAttemptsTable.pmがヒットした
つまり、少なくとも各提出結果を表示しているのはAttemptsTableであることが分かる
resultsを表示しているところ
Resultsはここではcorrect / incorrectなど、Attemptの評価結果を表している
rh_answersのソースを追うと、AttemptsTableのコンストラクタに与えられるanswers引数であることが分かる
AttemptsTableの呼び出し
Problem.pmが問題のGET/POSTのハンドルを行っている
param()でパラメータを取得している
たとえばcan_checkAnswersのソースコードを読むと、解答のAttemptが可能かどうかの判定処理が分かるようになっている
Form Processing
ここでフォームの内容 ⇒ 変数・オブジェクトへの変換を行っている
checkに関連していそうな処理
can_CheckAnswers(checkAnswersという名前で引き回し)を使っていて関連しそうな処理はここだけ
この処理ですぐAttemptsResultを呼び出している
この時点で解答のcheckは完了しているのだろうか
checkAnswersを引数に渡している別のサブルーチンでcheckが行われている可能性
で実行する処理のフラグをマージしていることから、この行より下に該当処理が存在する可能性が高い
renderPG
process_and_log_answers