課題の提出ファイルを一括ダウンロードする改良

課題の提出ファイルを一括ダウンロードする改良

- Minoru Kawano の投稿
返信数: 45

三重大学版Moodle 1.9+の4/28のスナップショットに、課題モジュールで「単一ファイルのアップロード」や「高度なファイルのアップロード」で提出された学生のファイルを、一括してダウンロードする機能を追加してみました。

もともとは、三重大学版Moodle 1.6.6の4/28のスナップショットのソースを見ていて、奥村先生らがすでに「ファイルのアップロード」の課題で提出されたファイルを、一括ダウンロードする機能を実現しておられました。それをもとに、1.9ベースに合うようにいくつか改良をした上で、さらに「高度なファイルのアップロード」にも対応できるようにしてみたものです。
まだ、不具合などあるかもしれません。その場合は、お知らせください。

利用するには、まず、添付している「download_submissions.patch.gz」を解凍し、mod/assignment/lib.phpに対してパッチを当ててください。あと、日本語の言語パック(例えば lang/ja_utf8/)のmoodle.phpの最後に次のような内容を、追記してください。

$string['downloadallassignment'] = '全ての課題をダウンロードする';

利用方法は、提出課題を確認する画面で、右上にある「すべてのコース評定を表示する」の下に「すべての課題をダウンロードする」というリンクが現れます。クリックすると、「submission.zip」というファイルをダウンロードできます。ダウンロードしたファイルを展開すると、ユーザ名ごとのフォルダの中に、ユーザが提出したファイルがあります。

内部的には、ダウンロードする各ユーザのファイルの名前をシフトJISに変換しています。もしかすると、Macの環境では問題があるかも知れません。その場合は、mb_convert_encoding関数を使っている箇所を、適当に変更していただければいいかと思います。

最後に、改造をきっかけをくださった、奥村先生はじめ三重大学版Mooodleの開発にかかわった方々に、お礼を申し上げます。

(編集 Tatsuya Shirai - 最初の投稿日時 2008年 05月 17日(土曜日) 16:27)

http://moodle.org/mod/forum/discuss.php?d=67425 より分割しました.

最大評点: お役立ち度: ★★★★★★★ (1)
Minoru Kawano への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
わぁ! ありがとうございます。m(__)m
さっそく三重大学でも使わせていただきます。
Haruhiko Okumura への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
ついでに(というか),1.9.1が出ていたので,マージしました。
けっこう大きな変更があるようで,うまくいっているか心配です。
とりあえず TeX Forum はこれに更新しました。たぶん評定で学籍番号が出てこないといった不具合があると思います。どなたか直していただければ幸いです。
Haruhiko Okumura への返信

Re: 三重大学版Moodle 2007年度版

- Takayuki Fukuyama の投稿
福山です

svnからcheckoutして少し使っていて気付いたのですが、IEでドロップダウンが上手く動作しませんでした(編集モードの「活動の追加」などで行うセレクトボックスを操作するものです)。
調べてみるとlibの下のdropdown.jsというファイルを呼んでいるようですがこれが無いようです。(IEとOperaのみ使われるみたいです)どうも上手くaddされてないような気がします。オリジナルのソースを被せてみた所結構「?」が付くものがありました。私の勘違いでしたら申し訳ありません。
Takayuki Fukuyama への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
あららら,すみません,チェックします。

Haruhiko Okumura への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
全部見直しました。いつの間にこんなに新しいファイルが増えていたんでしょう。^^;
それとも最初のインポートのミスなのかもしれません。
そういえば1.9.1にしたときに新しいファイルをチェックせずにcommitしてしまいました。^^;
けっこうたくさんの*.htmlファイルがバイナリ扱いされて入ってしまいました。どうしてなんでしょう。またそのうち見直します。

Minoru Kawano への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
> もしかすると、Macの環境では問題があるかも知れません。

Mac OS Xはファイル名がUTF-8ですので,unzipコマンドで展開すると問題が生じますが,ダブルクリックすると立ち上がる「アーカイブユーティリティ」やStuffIt Expanderはちゃんとファイル名を変換してくれるようです。
Haruhiko Okumura への返信

Re: 三重大学版Moodle 2007年度版

- Minoru Kawano の投稿

kawanoです。

早速ですが、先日公開したパッチに一部不具合がありましたので、新しいパッチを公開させていただきます恥ずかしい。日本語のみのファイル名の場合、例えば「テスト.doc」がファイル名の場合、ダウンロード後には「.doc」と拡張子だけ残るとという不具合がありました。それを改善しました。

すでにパッチを適用された方は、パッチを適用した後の、mod/asssignment/lib.phpの1115行目を次のように、

$dst = $userdir.'/'.mb_convert_encoding(array_pop(explode("/",$data['filepath'])), 'SJIS-win', 'UTF-8');

また、1253行目を次のようにしてください。

$dst = $userdir.'/'.mb_convert_encoding(array_pop(explode("/",$file)), 'SJIS-win', 'UTF-8');

いずれも太字の箇所が、改善した部分です。前のパッチでは、basename関数を使ってファイル名を取得していたのですが、前述のようにうまくいかない場合があるので、ローテクですが、array_pop関数とexplode関数を組み合わせました。

奥村先生>

> Mac OS Xはファイル名がUTF-8ですので,unzipコマンドで展開すると問題が生じますが,
> ダブルクリックすると立ち上がる「アーカイブユーティリティ」やStuffIt Expanderは
> ちゃんとファイル名を変換してくれるようです。

了解しました。ひと安心しました笑顔

Minoru Kawano への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
Kawano様,どうもありがとうございます。うちのSubversionレポジトリも更新しておきました。
Haruhiko Okumura への返信

Re: 三重大学版Moodle 2007年度版

- Tatsuya Shirai の投稿

 いま,この一括ダウンロードの機能をfs_moodleにも組み込もうとしています.

 いいですね,これはなかなか.クイック評定を行なうだけでなく,学生がアップロードしたファイルのバックアップを取るのにも使えます(スナップショット的にも).

 少し気になったのはダウンロードするファイル名です.
 いまはsubmission.zipで統一されていますよね.

 ここのところは好みがあるかとは思いますが,

//      header("Content-Disposition: attachment; filename=submission.zip");
        header('Content-Disposition: attachment; filename="'.convert_download_filename_encoding($assignment->name).'.zip"');  // T.Shirai

のように,当方の環境では$assigment->name+".zip"にしてみました.沢山の課題を次から次へと一括ダウンロードする際に,ファイル名を一つ一つ変える手間が省けます.

 なお,convert_download_filename_encoding()は自作の関数です.少しずつ改良を加え,いまはこのような関数になっています.

// ブラウザに合わせてダウンロードファイル名のフォーマットを変更する
function convert_download_filename_encoding($filename) {
    // Windowsでは使用できない半角文字をアンダーバーに置き換える
    $filename = mb_ereg_replace('[<>"\|:\\/\*\?]', '_', $filename);

    if (check_browser_version('MSIE')) {
        if (strlen(rawurlencode($filename)) > 21 * 3 * 3) {
            $filename = mb_convert_encoding($filename, "SJIS-WIN", "UTF-8");
            $filename = str_replace('#', '%23', $filename);
        } else {
            $filename = rawurlencode($filename);
        }
    } else if (check_browser_version('Safari')) {
        $filename = "";
    }
    return $filename;
}

課題の名前に,':'などを使っているとダウンロードしたファイル名がおかしくなる(化ける訳ではなく,短くなる)ので,それらはアンダーバーに変換しています.

 あぁ,いま気付きましたが,上記の変更だけではSafariは仲間外れ(submission.zipでダウンロードされる)ですねウインク

#このディスカッショントピックは非常に長くなってしまい読みにくいので,本件だけ独立するようにトピックスを分割してよろしいでしょうか?

Tatsuya Shirai への返信

Re: 三重大学版Moodle 2007年度版

- Haruhiko Okumura の投稿
> #このディスカッショントピックは非常に長くなってしまい読みにくいので,本件だけ独立するようにトピックスを分割してよろしいでしょうか?

ほんと長くなってしまいましたね。こういうのこそ,開発のところに移動してもいいかもしれません。
Haruhiko Okumura への返信

Re: 三重大学版Moodle 2007年度版

- Tatsuya Shirai の投稿

 分割させて頂きました.

 でも,さすがにフォーラムを越えての移動は無理そうですね複雑な


 恐るべし,Moodle.移動できました青あざ

Tatsuya Shirai への返信

Re: 三重大学版Moodle 2007年度版

- Tatsuya Shirai の投稿

 一応,fs_moodleでも動くようになりました.

 ユニークなフォルダをTEMPフォルダの下に作成し,ファイルをユーザごとのファイルをコピーしますよね.このユニークなフォルダの下に課題の名前($assignment->name)を元にしたフォルダ(Windowsでフォルダ名として適切な文字や半角スペースを_に変換し,さらに複数個以上続く_を一つの_に置き換える)を作成し,その下にユーザごとのファイルをコピーするようにしました.
 ZIP書庫を作成するのはこの課題名を元にしたフォルダ,削除はユニークなフォルダごと.

 添付ファイルは出力したZIPファイルをアーカイバ(Explzh)で見た状態です.ZIP書庫名も課題名を元にしたフォルダ名に”.zip”を付け足したものです.


コードを確認していて気になった点がありましたので報告します.
(私が間違えている可能性も大ですウインク

ZIP書庫の作成です.

        $archive = new PclZip($tmpdir.'/submission.zip');
        $target = $tmpdir;
        $v_list = $archive->create($target, PCLZIP_OPT_REMOVE_PATH, $tmpdir);
        if ($v_list == 0) {
            error_log("Error : ".$archive->errorInfo(true));
            $this->rmdir_r($tmpdir);
            exit;
        }

        $zip = file_get_contents($tmpdir.'/submission.zip');

こちらが該当箇所です.

 この最後の$zip ですが,当初はこの変数に読み込んだデータをechoする予定だったのではないでしょうか? その後,この$zipを使わずに,

readfile($tmpdir.'/submission.zip');

で出力していますよね.たぶん,$zip = の行は無くても動作するのではないでしょうか?

 あと,その上にあるZIP書庫作成の部分ですが,

        $files = array();
        $files[] = $tmpuserdir;
        if (!zip_files($files, $zippath)) {
            error_log("Error : Cannot create zip file");
            $this->rmdir_r($tmpdir);
            exit;
        }

のように,zip_files()関数を使うと簡単に記述できます.エラーコードは戻ってきませんが,その代わりにzip_files()関数の方でエラーメッセージを出力してくれます.ここは好き好きだと思うのですが,WindowsXPのサーバ(私が開発している環境)では,zip書庫がフルパスで作成されてしまう問題(:これは私の中途半端な改造が原因かも知れません)も発生してしまいましたので,zip_files()関数を使う方式を当方ではとっています.


(補足)

 上記コードは私の改造したプログラムに基づいていますので,河野先生の書かれたコードと置き換えても正しく動作しません.

 $tmpuserdir,$zippathは以下の通りにしています.

        $uniqid = md5(uniqid(rand(), true));
        $tmpdir = $CFG->dataroot.'/temp/'.$uniqid;
        $zipbasename = mb_ereg_replace('[<>"\|:\\/\*\? ]', '_', $assignment->name);
        $zipbasename = mb_ereg_replace('_+', '_', $zipbasename);
        $zipname     = $zipbasename.'.zip';
        $zippath     = "$tmpdir/$zipname";
        $tmpuserdir  = "$tmpdir/$zipbasename";

まず$tmpdirをmkdir()し,次に$tmpuserdirをmkdir()します.あとは$userdirを

//          $userdir = $tmpdir.'/'.$data['username'];
            $userdir = $tmpuserdir.'/'.$data['username'];

のように$tmpdirから$tmpuserdirに変えていますので,課題名を元にしたフォルダの下にユーザ名のフォルダがmkdir()され,ファイルがcopyされます.

添付 WholeDownload.jpg
Tatsuya Shirai への返信

Re: 三重大学版Moodle 2007年度版

- Minoru Kawano の投稿

河野です。返事がずいぶん遅れてしまい、すみません恥ずかしい

白井先生がご指摘の1つめについてですが、

$zip = file_get_contents($tmpdir.'/submission.zip');

の行は、たしかに参考にした三重大学版Moodle 1.6ではそのあとにechoがありました。ご指摘のとおり、この行はなくても問題ないと思います。

また、zip書庫の作成についても、zip_files関係のほうがスマートですね。実は、assignmentモジュールのディスカッションで、今回とは違う方法で、提出された課題に加えて、オンライン課題の内容もzipファイルでダウンロードできるようにしているパッチが開発されているようです。

http://moodle.org/mod/forum/discuss.php?d=31899

こちらでも圧縮は、zip_files関数が使われています。

Minoru Kawano への返信

Re: 課題の提出ファイルを一括ダウンロードする改良

- Katsunori Mizuno の投稿

このパッチ(Ver1.1)を、本家の Moodle 1.9.1 に当てて使ってみたのですが、単一ファイルの課題では、ダウンロードできるのですが、「高度なファイルのアップロード」では、空のZIPファイルがダウンロードされてしまいます。

本家の Moodle でも「高度なファイルのアップロード」で一括ダウンロードができるとうれしいのですが、どなたか既に対応された方がいらっしゃいましたら、変更箇所を教えて頂けないでしょうか。

よろしくお願いします。

Katsunori Mizuno への返信

高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 確かに.当方(fs_moodle版)の環境でも高度なファイルのアップロード時には,中身のないzipファイルが作成されますね.私も確認が不十分でした.明日,時間が取れるようならばチェックしてみますが,もし既に解決済みの方がいらっしゃったら,情報提供よろしくお願いします.

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 とりあえず,

    function download_all_submissions()の,

if (($ausers = get_records_sql($select.$sql.$sort,0,0)) !== false)

この出力結果である,$ausersがboolのfalseであることが原因ですね.

データベースの構造と照らし合わせながらチェックしてみます.


Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 高度なアップロードの場合,

            $sql = 'FROM '.$CFG->prefix.'user u '.
            'LEFT JOIN '.$CFG->prefix.'assignment_submissions s ON u.id = s.userid AND s.assignment = '.$this->assignment->id.' '.
            'WHERE s.numfiles > 0 AND u.id IN ('.implode(',', $users).') ';

ここで条件に加えている,s.numfiles が,データベースを見てみると(ファイルをアップロードしているにも関わらず)ゼロですね.それでヒットしないようですね.


mdl_assignment_submissionsのレコードは,

  • id: 各提出毎に割り振られるidなので非常に大きな数.
  • assignment:これが課題の識別に使われるidで同じ課題ならば同じ番号
  • userid:その課題を提出した学生のuserid
  • timecreated:初めて提出した日時
  • timemodified:修正した日時
  • numfiles:本来ならばこれが提出されたファイルの数を表すと期待される.
  • data1:謎.空欄.
  • data2:高度なアップロードで,”下書き”の時は空欄.”提出”したら'submitted'になる.
  • grade: 評価点
  • submissioncomment:コメント
  • format:良く分からないが,”下書き”なのにファイルが一つもない学生(削除した?)は0, それ以外は全て 1.
  • teacher: 教師のユーザid.コメントを記入した教師の識別かな?
  • timemarked: 多分,コメントを記入した日時でしょうねぇ.
  • mailed:教師へのメールか,学生へのメールか,formatが1ならばこれも1,0ならば0.

といったところです.numfilesが(我々の期待する)使われ方をしていないのが気になります.パッと調べた感じでは,”単一ファイルのアップロード”の場合はともかく,”高度なファイルのアップロード”の場合,numfilesを使っては期待した効果が得られなさそうです.

 選択肢は2つあります.

  1. formatが1のレコードのみ抽出してアーカイブを作製する.
  2. formatが何だろうと,mdl_assignment_submissionにレコードが登録されている以上,”下書き”してから全て消した,ことも情報として含めてアーカイブを作成する.(ただし,その場合は提出課題のtempフォルダへのコピーの際にファイルがコピーできなくてもエラーは出さないようにしないといけない)

個人的には1を選択します.

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 現象の把握は終わりました.では,対策ですが.

  1. numfilesの存在理由からきちんと解析する.
  2. formatが1ならいいじゃん,と深く考えずに対策する.
  3. formatの存在理由もきちんと解析してから対策する(いまさらnumfilesを0から3とか4とかに書き換えるのは危険すぎる).

個人的には2を選択します.

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 修正箇所です.

 mod/assignment/lib.phpのfunction download_all_submissions(),

    /// Construct the SQL
        if ($users) {
            $select = 'SELECT u.id, u.username, u.firstname, u.lastname, s.id AS submissionid, s.grade, s.numfiles, s.timemodified, s.timemarked, ((s.timemarked > 0) AND (s.timemarked >= s.timemodified)) AS status ';  // okumura & kawano
            $sql = 'FROM '.$CFG->prefix.'user u '.
            'LEFT JOIN '.$CFG->prefix.'assignment_submissions s ON u.id = s.userid AND s.assignment = '.$this->assignment->id.' '.
//          'WHERE s.numfiles > 0 AND u.id IN ('.implode(',', $users).') ';
            'WHERE s.format > 0 AND u.id IN ('.implode(',', $users).') ';

            /// Construct the SQL
            if ($sort = flexible_table::get_sql_sort('mod-assignment-submissions')) {
                $sort = 'ORDER BY '.$sort.' ';
            }

s.numfilesをs.formatに変更することで,下書きであれファイルを提出済みの学生のデータをアーカイブできることを当方の環境では確認できました.

 

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Katsunori Mizuno の投稿

本学のサーバで私が設定した課題では、assignment_submissions の format は、全て 0 になっています。

採点のために送信する」ボタンを有効にするの設定が「NO」で課題を設定していますが、これが関係あるのでしょうか?

やはり、numfiles が 1 以上になってくれるのが、正しい動きのように思います。

Katsunori Mizuno への返信

Re: 高度なファイルのアップロードへの対応

- Katsunori Mizuno の投稿

いっそのことと思い、以下のように、numfiles も format も見ないようにしたところ、

            'WHERE u.id IN ('.implode(',', $users).') ';

無事、一括ダウンロードできるようになりましたが、これで良いのか、自信はありません。

Katsunori Mizuno への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 そうそう,そうなんです! 先ほどの書き込みにウッカリ忘れていたのですが,もしかしたら環境(どのバージョンで作成した課題かなど)によってデータベースの使われ方が違いのではないか,という危惧がありました.

 Mizunoさんの環境では,まさにこの危惧が的中していますね.(やはり解析が必要?)

 Moodle1.9.1+のソースをgrepして見た限りでは,numfilesをまともに扱っている箇所が意外と少なかったので,もしかしたら使わなくなっていくのかな?などと考えていました.でも,formatというレコードもかなり意味不明ですからね...

>  'WHERE u.id IN ('.implode(',', $users).') ';

この場合は,一旦課題をアップロードした(下書き状態)けれども,やっぱり不安になってファイルを全て削除してしまった,学生も含まれると思います.つまりフォルダの中身が空の場合がある.私はそれでも構わないと思っています.その際にcopy()がしくじらなければ良い訳ですから.

 ちなみに,formatが0のときに,numfilesも0でしたか? > Mizunoさん

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Katsunori Mizuno の投稿
はい、format と numfiles の両方が 0 でした。
Katsunori Mizuno への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 ううむ.水野先生の対策が無難そうですね.

 私はいま別件の改良を行っていますので,どなたかnumfilesやformatがどのような目的で使われており,どのようなタイミングでどう変化するのか,解析して頂けないでしょうか.やはり気になります.

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Minoru Kawano の投稿

河野です。

numfilesを入れた理由ですが、たしか手元の環境(三重大学版の1.9+ベースの 4月28日のスナップショット版)では、numfilesがない状態ではダウンロードしたzipファイルの中身が空だったため、課題が提出されている学生だけダウロードしたかったので、データベースを参考にして追加しました。白井先生と水野先生のご推察どおりです。

いま、同じ環境のデータベースのほうを見ていますが、numfilesにはファイルの数が書かれています。formatのほうは、0だったり1だったりでソースを追いかけないとわからないですねぇ…

ともかく、1.9.1になってから挙動が変わっている可能性もありそうです。

Minoru Kawano への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 確かにちょっとイヤですよね,このデータベースのレコードの使い方は.(気持ちは分かるのですが)

 もし課題を提出した学生のみ,とするのでしたら,もう腹を括って,フォルダを作成してファイルをコピーする際に,一個もファイルをコピーしなかったら配列からuserのデータを消し,さらにフォルダも消す,という手が汎用的な気がします.手間ですけれどもね!

 とりあえずデータベースにデータが存在する,ということは,なんらかの提出するアクションをとった,という意味として私は空っぽのフォルダもアーカイブしてしまいます.

Minoru Kawano への返信

Re: 高度なファイルのアップロードへの対応

- Minoru Kawano の投稿

formatについてですが、mod/assignment/lib.php を見てみると、88行目あたりに

$this->defaultformat = FORMAT_HTML;

$this->defaultformat = FORMAT_MOODLE;

とか記述がありますね。これ、lib/weblib.php でそれぞれ 1 と 0 に定義されていますから、教員からのコメントなどのフォーマットをあらわしているのではないでしょうか。

また、同じ lib.php にある prepare_new_submission 関数をみると、初期値に0を設定しています。

じゃぁ、実際どうかとデータベースを見ると、HTMLのタグが入っている教員からのコメントがまったくなくても format が 1 になっていて、よく挙動がわかりません悲しい

Minoru Kawano への返信

Re: 高度なファイルのアップロードへの対応

- Katsunori Mizuno の投稿

こちらのテスト環境は、CentOS 4.6 に本家の Moodle 1.9.1+で 5/28日にダウンロードしたものですが、「高度なファイルのアップロード」では、numfiles は 0 のままです。

先ほど、三重大版を入れて同じようにテストしてみたのですが、こちらも numfiles は 0 のままで、本家のものと変わりませんでした。

こちらの環境では、本家と三重大版の両方とも、単一ファイルのでは、

mod/assignment/type/uploadsingle/assignment.class.php の中に

$submission->numfiles = 1;

$newsubmission->numfiles = 1;

という行があるので、numfiles が 1 になるのは、分かるのですが、高度なファイルのアップロードでは、

mod/assignment/type/upload/assignment.class.php

mod/assignment/type/upload/notes.php

の中に、numfiles を変更しているような部分がないので、どこで numfiles を変更しているのか探せませんでした。

kawanoさんのところでは、numfiles にファイル数が入るということですが、どこでこの値を入れているか分かりますか?

Katsunori Mizuno への返信

Re: 高度なファイルのアップロードへの対応

- Minoru Kawano の投稿

手元の三重大版で、高度なファイルのアップロードのテストをしてみました。

まず、format については、データベースを見ながらテストして、なんとなく予想がつきました。やはり教員のコメントのフォーマットをあらわしているようです。クイック評定でコメントを入力した場合は、「0」のままです。「状態」の「評定」や「更新」をクリックして、エディタから入力した場合は、「1」になりました。

numfilesのほうは駄目ですね…

もしかすると私の手元の環境のせいかもしれません。手元の環境は、もとは三重大版1.6+に、「高度なファイルのアップロード」相当の機能を追加した状態で使っていたのを、三重大版1.9+にアップグレードしたのです。もしかすると 1.6+のときに追加した機能では numfiles に数字が設定していたのかもしれません。確定ではないですが…

もう少し調べてみます。

Minoru Kawano への返信

Re: 高度なファイルのアップロードへの対応

- Minoru Kawano の投稿

ビンゴでした。http://moodle.org/mod/forum/discuss.php?d=95043&parent=422719 で紹介した、「高度なファイルのアップロード」相当の機能のほうでは、mod/assingment/type/upload のファイルに、次のような記述がありました。

assignment.class.php (147行目) : $submission->numfiles ++;
assignment.class.php (161行目) : $newsubmission->numfiles = 1;

ファイル内の場所は、mod/assingment/type/uploadsingle/assignment.class.php で numfiles を扱っている場所と大体同じです。

さて、同じような処理を 1.9+ にも追加できそうか、ソースを見てみましたが、mod/assingment/type/uploadsingle/assignment.class.php と mod/assingment/type/upload/assignment.class.php では、かなり処理が違いますねぇ。難しそうです。 

Minoru Kawano への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

 しかし,本家の方ではこの値を使っていない(使い忘れている?)というところでしょうか.

 私が検証用に使ったコースは1年前から半年前の間に学生が課題を提出するのに使ったデータベースですので,多分,Moodle1.8です.その頃から高度なファイルのアップロードで,numfilesを使わなくなっていた(=0),ということかな?

以下は蛇足です.

 何個のファイルがアップロードされたかをキチキチとDBで管理することにどれくらいの意味があるか,ですよね.ちなみに単一のファイルのアップロードで,古いファイルを更新するコードにバグ(fs_moodleのバグ)のあった時に,単一であるにも関わらず複数のファイルがアップロードされた(クイック評定でリストした際に)おかしな状態になりました.でも,それで誤動作するでもなく「わはは,変なの」で済んだのは,あまり厳密にファイルの数を管理するようなコードになって無かったお陰かも知れません.
 もし,numfilesがアップロードされたファイルの数を厳密に表し過ぎていると,実際のファイルの数(たとえば管理者が消してしまうなど)と一致しないと問題が発生するかも(それを発見するには良い?).それなら良いのですが,もしプログラムのミスやデータベースの損傷でリストアした場合に,実際のファイルの数と一致しない,という状況は発生し得ますね.

Tatsuya Shirai への返信

Re: 高度なファイルのアップロードへの対応

- Minoru Kawano の投稿

Assinment moduleのディスカッション(http://moodle.org/mod/forum/discuss.php?d=31899)に出ていた、パッチも見てみましたが、numfiles全然使っていませんね。

白石先生のおっしゃるように、厳密な管理をしだすときりがないないなぁと思いました。そういえば、numfilesを減らす処理もなかったように思いますし。

そうなると、水野先生の改良が現実的でよいですね。

Minoru Kawano への返信

Re: 高度なファイルのアップロードへの対応

- Tatsuya Shirai の投稿

http://moodle.org/mod/forum/discuss.php?d=98436

なんとなく偶然とは思えないようなタイミングでnumfilesが話題に上った上に,Trackerにまで.うーん,numfilesでアップロードしたファイルの数を管理することになるのだろうか? あまり感心できませんねぇ.無用なトラブルを呼ぶだけのような気がするのだけれども.

Minoru Kawano への返信

課題を一括ダウンロードする改良(v1.2)

- Minoru Kawano の投稿

河野です。

白井先生らのアドバイスや改良を参考に、課題モジュールで「単一ファイルのアップロード」や「高度なファイルのアップロード」で提出された学生のファイルを、一括してダウンロードする機能を改良しました。今回は、「オンライン課題」もダウンロードできるようにしました。
なお、あとで書きますが、動作に若干問題がある部分があります。ご注意ください。

今回は、2つパッチがあります。Moodle 1.9.2のオフィシャルリリース(Build: 20080711)のパッチと、三重大学版Moodle 1.9.2(Build: 20080723)へのパッチです。これは、三重大学版にはすでに以前公開した改良が適用されているためです。

利用するには、次のようにしてください。

  1. 添付している「lib.php.patch.tgz」をダウンロードして、mod/assignmentで解凍する。
  2. 「lib.php.orig.diff」と「lib.php.moodle19.diff」というファイルができあがる。
    1. オリジナルのMoodle 1.9.2 を利用している場合は、「lib.php.orig.diff」を「lib.php」に対してパッチを適用する。
    2. 三重大学版Moodle 1.9.2 を利用している場合は、「lib.php.moodle19.diff」を「lib.php」に対してパッチを適用する。
  3. オリジナルのMoodleの場合は、日本語の言語パック(例えば lang/ja_utf8/)のmoodle.phpの最後に次のような内容を、追記する。
    $string['downloadallassignment'] = '全ての課題をダウンロードする';

利用方法は、以前公開したのと同じです。提出課題を確認する画面で、右上にある「すべてのコース評定を表示する」の下に「すべての課題をダウンロードする」というリンクがあらわれるので、クリックすれば、課題を一括してダウンロードできます。
ダウンロードされるファイルの名前は、「20080916_第○会の課題.zip」というように、「ダウンロードした日付_課題の名前.zip」となります。ダウンロードしたファイルを展開すると、ユーザ名ごとのフォルダの中に、ユーザが提出したファイルがあります。オンライン課題の場合は、「ユーザ名.txt」というファイルが作られ、その中に入力した内容が書かれています。

最後に、動作上の問題点ですが、現在のところWindows XP上でIE6,7とFirefox3.0で動作を確認しています。Google Chromeでは、ダウンロードされるファイル名の日本語部分が文字化けします(圧縮された内容は問題ありません)。
また、なぜファイル名の最初にダウンロードした日付を入れたかという点ですが、白井先生のように、最初は「課題の名前.zip」としようと思ったのですが、「第○回」という課題名の場合、ZIPファイルの名前が「○回」となってしまう問題が発生しました。ちょっと改善策が思いつかなかったため、今回のような仕様になっています。この点について、アドバイスをいただけると幸いです。

Minoru Kawano への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Tatsuya Shirai の投稿

 関係ないかも知れませんが,fs_moodleでは以下の改良をZIPの処理に関して行っています.特に(2)の方はドライブレターの処理に関わる,つまりファイル名の頭の方に対して行なう処理のような気がします(記憶があやふや).ですので,このあたりを中心にチェックしてみては如何でしょうか.

http://www.suzuka-ct.ac.jp/mech/moodle/mod/wiki/view.php?id=320&page=view/lib%2Fpclzip%2Fpclzip.lib.php


      $p_path = fsCharset2CurrentCharset($p_path);  // (ADD)
      $p_path = CurrentCharset2fsCharset($p_path);  // (ADD)

は,$p_pathがシフトJISの場合はUTF-8に,一旦戻してから処理を行い,再びシフトJISに戻しています.もともと$p_pathがUTF-8ならばこの処理は不要です.もっとも,$p_pathがUTF-8ならばオリジナルのコードでも問題は発生しないとは思います.ただ,ZIP関係でファイル名を破損する可能性のある箇所がこのあたり,という程度の情報として参考にして下さい.

Minoru Kawano への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Hiroshi Ueda の投稿
群馬大学の上田です。

三重大学版Moodle 1.9.4+(2007101542) をRHEL4にて利用させていただいております。ありがとうございます。
課題一括ダウンロードの機能を便利に使わせていただいておりますが、課題によっては
「申し訳ございません。ファイルが見つかりませんでした。」というエラーになります。

「課題名」に「+」と「&」が含まれるとこうなる、というところまでつきとめました。

現在、mod/assignment/ 中のファイルを修正しようとしていますが、当方の力不足で
今も悩んでおります。アドバイスいただければ幸いです。

Hiroshi Ueda への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Haruhiko Okumura の投稿
バグレポートありがとうございます。
この機能を作ってくださったかたがここを見ておられるはずですので,直していただけることを期待しています。 笑顔

Haruhiko Okumura への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Takayuki Fukuyama の投稿

福山です

開発版の方はメール購読されていなかったためにすっかり見落しておりました。

> 課題によっては「申し訳ございません。ファイルが見つかりませんでした。」というエラーになります。
> 「課題名」に「+」と「&」が含まれるとこうなる、というところまでつきとめました。

調査した感じで、従来版では常にsubmission.zipとしていたものが(日付)+課題名.zipとなった部分で挙動が乱れているようです。zipの作成をzip_files関数に投げるように変更されていますが、これによりzip_files関数内のclean_filename関数が呼ばれる事になります。これにより多くの文字がアンダーバーに置き換わってzipされますが(ご指摘の「+
」「&」などの他に「()かっこ」なども置き換わります)、元の変数が加工されていない為に copy($zippath, $tmpzippath); が失敗します。

        $zipbasename = mb_ereg_replace('[<>"\|:\\/\*\? ]', '_', $assignment->name);
        $zipbasename = mb_ereg_replace('_+', '_', $zipbasename);
        $zipname     = date("Ymd").'_'.$zipbasename.'.zip';
        $zippath     = $tmpdir.'/'.$zipname;

従いましてこの辺りのコードで行っているアンダーバーの変換はclean_filename関数に投げればそちらで整形されますから、この関数を使ってみてはいかがでしょうか

        $zipbasename = clean_filename($assignment->name);
        // $zipbasename = mb_ereg_replace('[<>"\|:\\/\*\? ]', '_', $assignment->name);
        // $zipbasename = mb_ereg_replace('_+', '_', $zipbasename);
        $zipname     = date("Ymd").'_'.$zipbasename.'.zip';
        $zippath     = $tmpdir.'/'.$zipname;

とすればcleanされた後のファイル名が取得できるので(逆に、この関数を使って予め取得しておかないとclean_filenameされた後のファイル名を取れなそう)、失敗はほぼしないと思われます(厳密にはこのbasenameにひっついてくるprefixやテンポラリディレクトリのパスもclean_filenameした方がいいかもしれませんが、この処理ではまず問題なく失敗する事は無いと思います。

また、現在では

$CFG->unicodecleanfilename = true;

以外の状態で利用した場合、課題に日本語が入っていると失敗してしまいます。この機能を利用する環境は概ねこのフラグがonになっているとは思いますが、これが付いていない状態でclean_filenameするとファイル名が落ちたり大部分のファイル名がアンダーバーに置換されてしまいます。この関数内だけでも強制的に

$CFG->unicodecleanfilename = true;

を立ててからclean_filenameしてやれば課題名に日本語が入っていても正常にzipを吐いてくれると思います。この関数は課題のファイル郡をまとめたzipを吐いて抜けるだけなので、この設定フラグを立てても他への影響は少ないと思われますが、いかがでしょうか。

Takayuki Fukuyama への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Tatsuya Shirai の投稿

 大元のところから行くと,+や&のような文字をオリジナルのMoodleは取り扱えないはずですので,そもそもアップロードした段階でファイル名を修正する必要があるのかも知れません.

#当方の環境はそれらの文字を使用可能となるような改造を行ったMoodleですので,ファイルの削除やリネームが可能です.しかしzip書庫の作成はできませんでした.対策が必要ですね.

Tatsuya Shirai への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Tatsuya Shirai の投稿

 ああ,すみません,思いっきり勘違いしていました.学生が提出した課題のファイルのファイル名に+や&を含んでいた場合ではなく,ですね.

 fs_moodleではfunction clean_filename()の条件を緩和して,ダブルクオート以外の文字をファイル名と使用可能としています.ですので,その問題は発生しませんでした.

 しかし勘違いしたお陰で,もっと致命的な(fs_moodle版固有の)問題点が二つも発覚しました.一つはWindows環境で外部zipコマンドを利用してzip書庫を作成する際(バックアップでも使われていますね...)に,zipコマンドに引数として渡されるファイル名やフォルダ名に&や+が含まれているとzipコマンドが正しく引数を受け取れないという問題(zipに失敗)です.指定されたフォルダ以下に含まれるファイル名やフォルダ名に&や+が含まれていても問題はありません(escapeshellarg()関係なので).対策しました.
 もう一つは,一括ダウンロードにおいて学生が提出したファイルが”日本語文字を含むファイル名”の場合にzip書庫に格納されない”というバグでした.これは,データベースには提出した記録が残っているのに実際にはmoddataにファイルが存在しない(管理者がサーバ上で手作業にて削除したなど)場合であっても処理がコンティニューするようにした条件部分です.この問題はWindows環境においてのみ発生するfs_moodle版の問題です.もしファイルが存在しなかったらコピーしない(できなくても気にしない)という条件部にfile_exists()とis_file()関数を使用していました.自分で開発しておきながら,fs化を忘れるという痛恨のミスでした.これも対策しました.

 以上,fs_moodleをWindows環境にて使用している場合にのみ発生する問題です.もし急ぎで対策版が必要な方はご連絡下さい.来週の水曜日以降に公開する予定の次の版では直っています.

Takayuki Fukuyama への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Minoru Kawano の投稿

福山さん、こんにちは。河野です。

福山さんの改善がベストですね 笑顔 福山さんの書き込みを参考に、次のようにしてみました。

修正前は、

        $zipbasename = mb_ereg_replace('[<>"\|:\\/\*\? ]', '_', $assignment->name);
        $zipbasename = mb_ereg_replace('_+', '_', $zipbasename);
        $zipname     = date("Ymd").'_'.$zipbasename.'.zip';
        $zippath     = $tmpdir.'/'.$zipname;

となっていたものを、次のようにしてみました。ダウンロードするファイル名の最初の日付部分をコースの省略名に変更してみました。また、$CFG->unicodecleanfilename の設定も入れています。

                        // fukuyama & mkawano
        // fukuyama & mkawano
       
        $zippath     = $tmpdir.'/'.$zipname;

私も lib/moodlelib.php を見て、clean_filename 関数の中身を確認しました。基本的に、アルファベットと数字以外の文字は「_」に変換してしまうようですね。この関数を使ったほうがすっきりしますね。ereg_replace 関数を使わないのも、若干ですが、パフォーマンス的に良いように思います。

Minoru Kawano への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Hiroshi Ueda の投稿
上田です。
ありがとうございました。
課題名に「&」や「+」が入っていても正常に課題のzipファイルがダウンロードできるようになりました。
Hiroshi Ueda への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Minoru Kawano の投稿

兵庫大学の河野です。ダウンロード機能を作ったものです。

上田先生が報告してくださったエラーですが、私のほうでも確認しました。
サーバはCentOS5.2で、三重大学版 Moodle 1.9.4+ (Build: 20090325) です。

応急処置になりますが、mod/assignment/lib.php の次の内容の行(3か所あります)

$zipbasename = mb_ereg_replace('_+', '_', $zipbasename);

を、次のように書き換えてみてください(赤字の部分)。課題名の「+」や「&」の部分を、「_」に置き換えてしまいますが、ダウンロードできるようになると思います。

$zipbasename = mb_ereg_replace('', '_', $zipbasename);

おそらく課題ファイルを圧縮する部分(pclzip)で、圧縮先のファイル名に「+」や「&」が貼っていてはいないためではないかと思います。ただ、はっきりした原因はつかんでいませんので、調査してみたいと思います。
とくに支障がないようでしたら、上の修正で対応していただければと思います。

Minoru Kawano への返信

Re: 課題を一括ダウンロードする改良(v1.2)

- Minoru Kawano の投稿

本筋とは無関係ですが、一部訂正です。恥ずかしい

  • 誤:貼っていてはいないためではないか
  • 正:入っていてはいけないためではないか

Minoru Kawano への返信

リストア時のログの処理

- Tatsuya Shirai の投稿

 あまり重要ではないかも知れませんが,ログ付きでバックアップしたコースをリストアする際に,

action (assignment-download all submissions) unknown. Not restored
action (assignment-download all submissions) unknown. Not restored
action (assignment-download all submissions) unknown. Not restored
action (assignment-download all submissions) unknown. Not restored
action (assignment-download all submissions) unknown. Not restored
action (assignment-download all submissions) unknown. Not restored

が延々と出ますね.

add_to_log($this->course->id, "assignment", "download all submissions",
           "submissions.php?id={$this->cm->id}", $this->assignment->id, $this->cm->id);

これらのログを書き戻す際の処理が,mod/assignment/restorelib.phpのfunction assignment_restore_logs()に該当するcase "download all submissions" を追加した方が良いかも知れません.

 すみません,ちょっと時間が無いので報告だけになります.