数式自動採点コンテンツの更新

18 10月

仕事の記録です。

次期、数式自動採点コンテンツ用にコードを書き直しています。scorm_portal に蓄えられている教材も内容を変更しなければなりません。そのためのツールを書きました。

大きな変更点としては、これまで暗号化したまま保存していた正解を復号化して可読の状態で保存することに変更しました。それは、今回OSのバージョンアップにともなって暗号化のパッケージが動かなくなって他のパッケージへの変更を余儀なくされたからです。暗号化したまま保存しておくと将来危険です。これからは、教材としてダウンロードするときに暗号化します。他の変更点は、採点phpのURLの変更や、配点タグの追加などでした。以下に、そのコードをあげます。いったんデーターベースをコピーしたあと、修正を加えます。

これを使って、今度の3月にデータベースを修正します。

<?php

// scorm_portal のデータを修正するプログラム。これまでは、正解が暗号化されたままだったが
// それでは、OSの変更などによって、暗号化パッケージが動かなくなった時に危険である。そのため、
// これからは、暗号化せずに正解を記録する。このプログラムで、暗号化された正解を復号して、
// データを新しい scorm_portal に適合するように修正する。

// 配点タグも追加

// データベースに接続
$DB = mysqli_connect( 'localhost', 'ユーザー名', 'パスワード', 'scorm_portal') or die(mysqli_connect_error());

mysqli_set_charset($DB, 'utf8');
// データベースに接続
$DB2 = mysqli_connect( 'localhost', 'ユーザー名', 'パスワード', 'scorm_portal_copy') or die(mysqli_connect_error());

mysqli_set_charset($DB2, 'utf8');

// コピー先($DB2)のデータベースの内容を削除する

$sql = "SHOW TABLES;";
$result = mysqli_query($DB2, $sql) or die(mysqli_error($DB2));

$rows = mysqli_num_rows($result);

echo "コピー先のTable ".$rows." 個を削除します\n";
for ($i = 0; $i < $rows; $i = $i + 1) {

$row = mysqli_fetch_array($result);

//var_dump($row[0]);

$sql = "DROP TABLE `".$row[0]."`;";

mysqli_query($DB2, $sql) or die(mysqli_error($DB2));

echo "DROP ".$row[0]."\n";

}

// コピーする

$sql = "SHOW TABLES;";
$result = mysqli_query($DB, $sql) or die(mysqli_error($DB));

$rows = mysqli_num_rows($result);

echo "Table ".$rows." 個をコピーします\n";
for ($i = 0; $i < $rows; $i = $i + 1) {

$row = mysqli_fetch_array($result);

//var_dump($row[0]);

//$sql = "DROP TABLE `".$row[0]."`;";

$a_table_name = "`".$row[0]."`";

$sql = "CREATE TABLE scorm_portal_copy.$a_table_name LIKE scorm_portal.$a_table_name";

mysqli_query($DB2, $sql) or die(mysqli_error($DB2));
$sql = "INSERT INTO scorm_portal_copy.$a_table_name SELECT * FROM scorm_portal.$a_table_name";

mysqli_query($DB2, $sql) or die(mysqli_error($DB2));
echo "copy ".$row[0]."\n";

}

// author table 名だけを取得し、配列に入れる
$sql = "SHOW TABLES LIKE 'table_%';";
$result = mysqli_query($DB, $sql) or die(mysqli_error($DB));

$rows = mysqli_num_rows($result);

echo "author の table は".$rows." 個あります\n";

$table_ary = array();
for ($i = 0; $i < $rows; $i = $i + 1) {

$row = mysqli_fetch_array($result);
$table_ary[] = $row[0];

}

$table_num = $rows;

//var_dump($table_ary);

// 作業フォルダーを用意する

$stamp = microtime();
list($msec, $sec) = explode(" ", $stamp);

$mustamp = $stamp*1000000;

$timestamp = $sec.$mustamp;

$head = "tmp".$timestamp;

$foldername = mb_convert_encoding($head, "UTF-8", "auto");

passthru("mkdir -p /var/www/html/temporary/$foldername/tmp/");

echo "作業フォルダー $foldername を作成\n";

// それぞれのコンテンツの正解を復号する

// 暗号の為

require_once '/var/www/html/Crypt_RSA-1.2.1/RSA.php';

function check_error(&$obj)
{
if ($obj->isError()){
$error = $obj->getLastError();
switch ($error->getCode()) {
case CRYPT_RSA_ERROR_WRONG_TAIL :
// nothing to do
break;
default:
// echo error message and exit
echo 'error: ', $error->getMessage();
exit;
}
}
}

$file = '/var/www/key/key.txt';

$keyxml = simplexml_load_string(file_get_contents($file));

for ($i = 0; $i < $table_num; $i = $i + 1) {

$a_table_name = "`".$table_ary[$i]."`";

$sql = "SELECT id, title, zip FROM $a_table_name;";

$result = mysqli_query($DB2, $sql) or die(mysqli_error($DB2));

$rows = mysqli_num_rows($result);

echo "$table_ary[$i] has ".$rows." rows.\n";
for ($j = 0; $j < $rows; $j = $j + 1){

$row = mysqli_fetch_array($result);

$id = $row['id'];

$a_file = fopen("/var/www/html/temporary/$foldername/tmp.zip","w");

fwrite($a_file, $row['zip']);

fclose($a_file);

passthru("cd /var/www/html/temporary/$foldername/; unzip -q /var/www/html/temporary/$foldername/tmp.zip -d /var/www/html/temporary/$foldername/tmp/");
decode_TAns($foldername);

passthru("cd /var/www/html/temporary/$foldername/tmp ; zip -q -r /var/www/html/temporary/$foldername/new.zip ./configfile.xml ./glossary ./html");

$zipbinary = file_get_contents("/var/www/html/temporary/$foldername/new.zip");

$zipbinary = mysqli_real_escape_string($DB2, $zipbinary);
$sql = "UPDATE $a_table_name SET zip='$zipbinary' WHERE id='$id'";
$result_UPDATE = mysqli_query($DB2, $sql) or die(mysqli_error($DB));

echo "changed ".$row['title']."\n";
// 作業ファイルを消す

passthru("rm -r /var/www/html/temporary/$foldername/*");
//break 2;

}
}
function decode_TAns($foldername) {

global $keyxml;
// TAns を復号

$config_text = file_get_contents("/var/www/html/temporary/$foldername/tmp/configfile.xml");
$config_xml = simplexml_load_string($config_text);
//$file = '/var/www/key/key.txt';
//$keyxml = simplexml_load_string(file_get_contents($file));
// private key を探す
if ($config_xml->key) {
$public_key = (string) $config_xml->key;

$key_num = 0;

for ($i = 0; $i < $keyxml->key->count(); $i++) {

if (((string) $keyxml->key[$i]->public) == $public_key) {
$key_num = intval($keyxml->key[$i]->number);
}
}

} else {

$key_num = 0;
$public_key = (string) $keyxml->key[$key_num]->public;

}

$key2 = Crypt_RSA_Key::fromString($keyxml->key[$key_num]->private);

$rsa_obj = new Crypt_RSA;
check_error($rsa_obj);
//Check encrypting/decrypting function's behaviour
$rsa_obj->setParams(array('dec_key' => $key2));
check_error($rsa_obj);
$i = 0;

foreach ($config_xml->question as $q) {
$i = $i + 1;

$someobj = new find_question();

$q = $someobj->action($rsa_obj,$q);
}
$config_xml->version = '3.0';

$config_xml->cgi->url = "/php_simple_question_recursion/answertest_3_0.php";

$config_xml->key = '';

$config_string = $config_xml->asXML();

$config_string = str_replace('<key></key>','<phpseclibKey></phpseclibKey>',$config_string);

$config_string = str_replace('<size>',"<Allotment>5</Allotment>\n<size>",$config_string);

//echo $config_string;

$target = fopen("/var/www/html/temporary/$foldername/tmp/configfile.xml","w");

fputs($target, $config_string);

fclose($target);

// html フォルダーの中の configfile.xml を修正

$config_text = file_get_contents("/var/www/html/temporary/$foldername/tmp/html/configfile.xml");

$config_xml = simplexml_load_string($config_text);
$config_xml->version = '3.0';

$config_xml->cgi->url = "/php_simple_question_recursion/answertest_3_0.php";

$config_xml->key = '';

$config_string = $config_xml->asXML();

$config_string = str_replace('<key></key>','<phpseclibKey></phpseclibKey>',$config_string);
$target = fopen("/var/www/html/temporary/$foldername/tmp/html/configfile.xml","w");

fputs($target, $config_string);

fclose($target);
} // decode_TAns
class find_question {

function __constructor() {
}

public function action($rsa_obj,$q) {
$tans =(string)$q->TAns;

$q->TAns = trim(str_decode($rsa_obj,$tans));

if ($q->question) {

$newq = $q->question;

$someobj = new find_question();

$newq = $someobj->action($rsa_obj,$newq);

}

return $q;
}

} // end of find_question
function str_decode($rsa_obj,$str) {

$rtext = (string)$rsa_obj->decrypt($str);

return $rtext;
}