Moodleに研究室の予定を書き込んでいるが、終日の予定は時刻をデフォルトの「00:00」のままで変更しない。特に終了時刻はわざわざ入力しない。これで「終日」になることを期待するが…。

これをGoogle Calendarにエクスポートすると、前日の終日予定に見えてしまう。これを回避するためには、00:00以外のそれより遅い時刻に設定すればよい。


環境はEmacs 24.5, Windows10である。init frameをメイリオで設定しているのだが、C-x 5-2で新しいフレームを開くとこんな風になる…。

そこで、MewやFiremacsで余りに有名な山本和彦さんの記事を参考に修正!山本さんありがとうございます。
[code];; C-x 5 2 で新しいフレームを作ったときに同じフォントを使う
(setq frame-inherited-parameters ‘(font tool-bar-lines))
[/code]
前々から行いたかった、LMSに蓄積されているログを対象データとした研究について。
行いたいことはテキストマイニング(これはこれで「重要文抽出」「キーワード抽出」「トピック抽出」「テキストの一貫性評価」「複数文書要約」「テキストクラスタリング」などの面白いトピックがある)ではない。LMSで提供しているSCORMコンテンツの改善を目的とし、データベースなど構造化されたものから、ユーザに着目した分析となるであろう。データマイニングと言えるかもしれない。たとえば、BovoらはEDM2013、ICEEE2013でユーザそれぞれについてのログイン回数、最終ログイン、学習時間、レッスン読了数、ダウンロード数、小テスト、課題の評定と各種平均を正規化したデータをWekaでクラスタリングしている。
Wekaについては、恩師である渡邊裕司先生もお使いであると仰っていた、それなりに実績のあるツールのようだ(本やOERもある)。森幹彦先生には、orangeというビジュアライズされたワークフローを作成できるPythonベースのツールを紹介していただいた(活用している方のブログ記事)。また、これらのツールを比較した解説もある(これとかこっちとかスライドも)。
ただし、ツールは夢のようなものではなく、それが何を行っているのか理解できない者が使うべきではない。当たり前だが、ツールを使うことが目的になってはならない。最終目的はコンテンツの改善である。ただ、ログなんぞから、ユーザの「学習」状況など分かるはずがない。しかしながら、全てをきちんと受講している「可能性」のある閲覧状態というのはあるはずである(受講していてもタレ流しかもしれないから)。
SCO単位の受講率と総合テストの成績の相関は容易に分かる。これに加えて上記2が分かれば、オンラインかつモチベーションがない、強制的にやらされる「○○○○についての学習」というシチュエーションで、ユーザの「飽き」と「作業ゲー状態」を検出することができるかもしれない。また、たとえば次のようにユーザを分類し、アダプティブなコンテンツとすることも考えられる。
決してバズワードの研究分野のつもりはありませんから!
と言っていいものか分からないが、行ったことをまとめておく。大したことはやっていない。SCORM受講ログの取得に十時間以上かかってしまったし、コースログの取得はPHPのmemory_limitとのたたかいであった。やはりPHPはパフォーマンスの問題がある。Try & Error の開発がしやすいこととのトレードオフなのだろうが…。
– $row[] = html_writer::link(new moodle_url("/user/view.php?id={$log->userid}&course={$log->course}"), $log->userid);
+ $row[] = html_writer::link(new moodle_url("/user/view.php?id={$log->userid}&course={$log->course}"), fullname($log, has_capability(‘moodle/site:viewfullnames’, context_course::instance($course->id))));
$displayaction="$log->module $log->action";
if ($brokenurl) {
@@ -531,7 +531,7 @@
$link = new moodle_url("/iplookup/index.php?ip=$log->ip&user=$log->userid");
echo $OUTPUT->action_link($link, $log->ip, new popup_action(‘click’, $link, ‘iplookup’, array(‘height’ => 400, ‘width’ => 700)));
echo "</td>\n";
– $fullname = $log->userid;
+ $fullname = fullname($log, has_capability(‘moodle/site:viewfullnames’, context_course::instance($course->id)));
echo "<td class=\"r$row c3\" >\n";
echo " <a href=\"$CFG->wwwroot/user/view.php?id={$log->userid}\">$fullname</a>\n";
echo "</td>\n";
@@ -619,7 +619,7 @@
$coursecontext = context_course::instance($course->id);
$firstField = format_string($courses[$log->course], true, array(‘context’ => $coursecontext));
– $fullname = $log->userid;
+ $fullname = fullname($log, has_capability(‘moodle/site:viewfullnames’, $coursecontext));
$actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
$row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.’ ‘.$log->action.’ (‘.$actionurl.’)’, $log->info);
$csvexporter->add_data($row);
@@ -730,7 +730,7 @@
$myxls->write($row, 0, format_string($courses[$log->course], true, array(‘context’ => $coursecontext)), ”);
$myxls->write_date($row, 1, $log->time, $formatDate); // write_date() does conversion/timezone support. MDL-14934
$myxls->write($row, 2, $log->ip, ”);
– $fullname = $log->userid;
+ $fullname = fullname($log, has_capability(‘moodle/site:viewfullnames’, $coursecontext));
$myxls->write($row, 3, $fullname, ”);
$actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
$myxls->write($row, 4, $log->module.’ ‘.$log->action.’ (‘.$actionurl.’)’, ”);
@@ -844,7 +844,7 @@
$myxls->write_string($row, 0, format_string($courses[$log->course], true, array(‘context’ => $coursecontext)));
$myxls->write_date($row, 1, $log->time);
$myxls->write_string($row, 2, $log->ip);
– $fullname = $log->userid;
+ $fullname = fullname($log, has_capability(‘moodle/site:viewfullnames’, $coursecontext));
$myxls->write_string($row, 3, $fullname);
$actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
$myxls->write_string($row, 4, $log->module.’ ‘.$log->action.’ (‘.$actionurl.’)’);
— mod/quiz/report/attemptsreport.php 2015-10-16 19:03:12.000000000 +0900
+++ mod/quiz/report/attemptsreport.php.20151016 2015-10-16 19:04:44.000000000 +0900
@@ -153,10 +153,10 @@
$headers[] = ”;
}
if (!$table->is_downloading()) {
– $columns[] = ‘userid’;
+ $columns[] = ‘username’;
$headers[] = get_string(‘name’);
} else {
– $columns[] = ‘userid’;
+ $columns[] = ‘username’;
$headers[] = get_string(‘name’);
}
— mod/scorm/report/interactions/report.php.20151020 2015-10-20 11:21:52.000000000 +0900
+++ mod/scorm/report/interactions/report.php.20151016 2015-10-16 12:33:37.000000000 +0900
@@ -418,9 +418,9 @@
$row[] = $OUTPUT->user_picture($user, array(‘courseid’=>$course->id));
}
if (!$download) {
– $row[] = ‘<a href="’.$CFG->wwwroot.’/user/view.php?id=’.$scouser->userid.’&course=’.$course->id.’">’.$scouser->userid.'</a>’;
+ $row[] = ‘<a href="’.$CFG->wwwroot.’/user/view.php?id=’.$scouser->userid.’&course=’.$course->id.’">’.$scouser->username.'</a>’;
} else {
– $row[] = $scouser->userid;
+ $row[] = $scouser->username;
}
if (empty($timetracks->start)) {
$row[] = ‘-‘;
— mod/scorm/report/basic/report.php.20151020 2015-10-20 11:04:48.000000000 +0900
+++ mod/scorm/report/basic/report.php.20151016 2015-10-16 11:02:03.000000000 +0900
@@ -121,7 +121,7 @@
$columns[]= ‘picture’;
$headers[]= ”;
}
– $columns[] = ‘userid’;
+ $columns[] = ‘username’;
$headers[] = get_string(‘name’);
$columns[]= ‘attempt’;
@@ -383,9 +383,9 @@
$row[] = $OUTPUT->user_picture($user, array(‘courseid’=>$course->id));
}
if (!$download) {
– $row[] = ‘<a href="’.$CFG->wwwroot.’/user/view.php?id=’.$scouser->userid.’&course=’.$course->id.’">’.$scouser->userid.'</a>’;
+ $row[] = ‘<a href="’.$CFG->wwwroot.’/user/view.php?id=’.$scouser->userid.’&course=’.$course->id.’">’.$scouser->username.'</a>’;
} else {
– $row[] = $scouser->userid;
+ $row[] = $scouser->username;
}
if (empty($timetracks->start)) {
$row[] = ‘-‘;
— mod/scorm/report/objectives/report.php.20151020 2015-10-20 11:23:52.000000000 +0900
+++ mod/scorm/report/objectives/report.php.20151016 2015-10-16 12:34:18.000000000 +0900
@@ -422,9 +422,9 @@
}
if (!$download) {
$row[] = ‘<a href="’.$CFG->wwwroot.’/user/view.php?id=’.$scouser->userid.
– ‘&course=’.$course->id.’">’.$scouser->userid.'</a>’;
+ ‘&course=’.$course->id.’">’.$scouser->username.'</a>’;
} else {
– $row[] = $scouser->userid;
+ $row[] = $scouser->username;
}
if (empty($timetracks->start)) {
$row[] = ‘-‘;
[/code]
— course/lib.php 2015-10-20 11:46:28.000000000 +0900
+++ course/lib.php.20151020 2015-10-20 11:26:45.000000000 +0900
@@ -558,7 +558,6 @@
$header = array();
$header[] = get_string(‘course’);
$header[] = get_string(‘time’);
– $header[] = get_string(‘time’);
$header[] = get_string(‘ip_address’);
$header[] = get_string(‘fullnameuser’);
$header[] = get_string(‘action’);
@@ -622,7 +621,7 @@
$firstField = format_string($courses[$log->course], true, array(‘context’ => $coursecontext));
$fullname = $log->userid;
$actionurl = $CFG->wwwroot. make_log_url($log->module,$log->url);
– $row = array($firstField, userdate($log->time, $strftimedatetime), $log->time, $log->ip, $fullname, $log->module.’ ‘.$log->action.’ (‘.$actionurl.’)’, $log->info);
+ $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.’ ‘.$log->action.’ (‘.$actionurl.’)’, $log->info);
$csvexporter->add_data($row);
}
$csvexporter->download_file();
— mod/quiz/report/attemptsreport_table.php 2015-10-20 11:20:12.000000000 +0900
+++ mod/quiz/report/attemptsreport_table.php.20151020 2015-10-20 11:01:19.000000000 +0900
@@ -163,7 +163,7 @@
*/
public function col_timestart($attempt) {
if ($attempt->attempt) {
– return $attempt->timestart;
+ return userdate($attempt->timestart, $this->strtimeformat);
} else {
return ‘-‘;
}
@@ -176,7 +176,7 @@
*/
public function col_timefinish($attempt) {
if ($attempt->attempt && $attempt->timefinish) {
– return $attempt->timefinish;
+ return userdate($attempt->timefinish, $this->strtimeformat);
} else {
return ‘-‘;
}
@@ -189,7 +189,7 @@
*/
public function col_duration($attempt) {
if ($attempt->timefinish) {
– return $attempt->timefinish – $attempt->timestart;
+ return format_time($attempt->timefinish – $attempt->timestart);
} else {
return ‘-‘;
}
— mod/scorm/report/interactions/report.php 2015-10-20 11:23:34.000000000 +0900
+++ mod/scorm/report/interactions/report.php.20151020 2015-10-20 11:21:52.000000000 +0900
@@ -435,14 +435,14 @@
$row[] = $scouser->attempt;
}
if ($download ==’ODS’ || $download ==’Excel’ ) {
– $row[] = $timetracks->start;
+ $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
} else {
– $row[] = $timetracks->start;
+ $row[] = userdate($timetracks->start);
}
if ($download ==’ODS’ || $download ==’Excel’ ) {
– $row[] = $timetracks->finish;
+ $row[] = userdate($timetracks->finish, get_string(‘strftimedatetime’, ‘langconfig’));
} else {
– $row[] = $timetracks->finish;
+ $row[] = userdate($timetracks->finish);
}
$row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
}
— mod/scorm/report/basic/report.php 2015-10-20 11:12:59.000000000 +0900
+++ mod/scorm/report/basic/report.php.20151020 2015-10-20 11:04:48.000000000 +0900
@@ -399,14 +399,14 @@
$row[] = $scouser->attempt;
}
if ($download ==’ODS’ || $download ==’Excel’ ) {
– $row[] = $timetracks->start;
+ $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
} else {
– $row[] = $timetracks->start;
+ $row[] = userdate($timetracks->start);
}
if ($download ==’ODS’ || $download ==’Excel’ ) {
– $row[] = $timetracks->finish;
+ $row[] = userdate($timetracks->finish, get_string(‘strftimedatetime’, ‘langconfig’));
} else {
– $row[] = $timetracks->finish;
+ $row[] = userdate($timetracks->finish);
}
$row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
}
— mod/scorm/report/objectives/report.php 2015-10-20 11:24:46.000000000 +0900
+++ mod/scorm/report/objectives/report.php.20151020 2015-10-20 11:23:52.000000000 +0900
@@ -439,14 +439,14 @@
$row[] = $scouser->attempt;
}
if ($download ==’ODS’ || $download ==’Excel’ ) {
– $row[] = $timetracks->start;
+ $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
} else {
– $row[] = $timetracks->start;
+ $row[] = userdate($timetracks->start);
}
if ($download ==’ODS’ || $download ==’Excel’ ) {
– $row[] = $timetracks->finish;
+ $row[] = userdate($timetracks->finish, get_string(‘strftimedatetime’, ‘langconfig’));
} else {
– $row[] = $timetracks->finish;
+ $row[] = userdate($timetracks->finish);
}
$row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
}
[/code]
「通研」とは、東北大学電気通信研究所のことで、光通信、八木宇田アンテナ、垂直磁気記憶、フラッシュメモリなど、数々の業績を積み重ねてきた研究所である。私などが紹介するまでもない。私の研究者としてのキャリアはここ通研からスタートしたため思い入れがある。そういう意味でも、通研が青葉山地区に移転することに一旦決まった時は寂しく思ったが、結局片平地区にとどまることになり、新棟が完成した。セミナーに登壇するため仙台を訪れたので素通りするわけには行かない。

内部は開放的な空間が広がり、うらやましいの一言。

菅沼研、木下研、FIRにおじゃましてきた。また、私を拾ってくださった鈴木陽一先生にご挨拶に伺ったところ、実験室を案内していただいた。しくみマニアとしては堪らない空間で凄いの一言。通研の皆様、本当にありがとうございました!


7月29日の京都会場に続き仙台でも登壇させていただいた。仙台は私のキャリアのスタートとなった思い出の地である。通研で拾ってもらっていなければ今の私は無いと断言できる。会場では思わぬ再会があり、懐かしい記憶が思い返された。

講演内容は京都会場とアウトラインは同じものの、内容を取捨選択したので時間内にSINET群馬ノードを移設した話(!)までできた。ITに限らず、このような「どうなるか分からない改革」は結局ユーザとの信頼関係が全て(つまり「何を言ったか」より「誰が言ったか」になる)。今はそのような関係を築く立場からは外れてしまったものの、これらの経験は私の仕事を進める上でのいちばんのモチベーションとなっている。
お招きいただきありがとうございました。NetSpring、ALE関係各位に御礼申し上げます。

ファルコンSCの山下さんから登壇依頼があり、「学認クラウドと連携ソリューション事例紹介セミナー」で「Shibboleth認証連携でOffice365 Educationを実運用するまでの長い道のり」と題して講演を行った。私自身3月末で運用から離れているため、これがほんとうに最後になると思っている。このところ登壇が続いているのと、講演時間が50分だったので余裕を持って喋れると思っていたのだが、それがあだになり尻切れトンボになってしまった(すみません)。
今回の聴衆は認証連携の人達のはずなので、どのような(ややこしい)仕組みでOffice365のShibboleth連携を行っているかについて、自分なりに整理して話すように努めた。それなりに歴史があるMicrosoftのオンラインサービスの品質について考えていただくきっかけになれば幸いである。
セミナー全体は、学認クラウドのご担当者が苦労されている話とか、福岡大学の生体認証の研究紹介など、ひじょうに興味のある話ばかりで、その後の懇親会も盛り上がった。主催者のファルコンSC、サイボウズ、日立電線ネットワーク各社様、またセッションに参加された皆様、どうもありがとうございました。

enPit関連のMLで知り、無理をして参加しました。実践入門とArchitecting…で5日間必要なのですが、3日間のみの参加です。それぞれは本当なら一日あたり$600ということなのですが、今回の企画は無料でした。「AWSはすごい」というのはよく聞きますし、通常使っている「なんちゃってクラウド」に嫌気がしており、AWSがなんぼのものか興味があったのです。
最初の2日で、EC2のインスタンスを立ち上げ、WordPressのスタティックコンテンツはS3に格納、その前段にCloudFrontによるキャッシュを実装しました。また、EC2のAuto ScalingやELBの機能について学びました。3日目はより詳しく、IAM、EC2、S3について、またオンプレをどのようにAWSに移行するかのディスカッションを行いました。
感想。今更ですがAWS凄いです。これは大学でプライベートクラウドとか構築してる場合ではありません。情報系センターの仕事がなくなります。一方、実際に学んでみてAWSが魔法のシステムではないということも分かりました。早速NASのバックアップにS3とGlacierを使おうと思います。
ハードルがあるとしたら、月額○○とか年額○○という料金体系に飼い慣らされてきた日本での従量課金でしょうか。今後どのような展開が待っているか楽しみです。

3年前にも登壇したセミナーに再度呼んでいただきました。これまでの3年半のマイクロソフトのオンラインサービスの運用を総括しました。つまり、CLE16の続編ということになり、スラドで「論文の体をなしていない」とか「雑文だ」とか批判されたことを反省し(いやそもそもCLEは研究会なんで論文ではなく、論文と言っているのはスラドなのですが)、もう少し体系的、つまりオンラインサービスは次の三要素からなっているという考えからまとめようとしたものです(講演資料)。
いずれの観点からも「否」と言いたいのではなく、ソフトウェアにはバグがつきものです。システム運用のミスは信頼を失う原因になります。しかし、サポート対応でこれらのマイナス面はいかようにでもなります(生身の人間ですから)。でも、サポートとのやり取りのメールが、意味不明で冗長な日本語の繰り返しだったらどうでしょう。最悪ですよね。たとえばこんなのです。
再発防止につきましては、短期的な対策の実施および、長期的な取り組みをそれぞれ実施している状況となります。具体的な内容や時期についてはお伝えする事ができずに申し訳ございませんが、引き続き改善に努めさせていただく所存でございます。
これ、何も言っていないのと同じですよね…。ユーザとのインタラクションがすべての印象を決めてしまうということを認識していないのでしょうか?