クラウドソーシングサービス×oTreeでのオンライン実験
普段から私は、クラウドソーシングサービスとoTreeで作った実験プログラムを使ってオンライン実験をしています。クラウドソーシングサービスで参加者を募集してoTreeでの実験プログラムへのリンクにアクセスしてもらい、実験プログラムの最後に表示されるコードを入力してもらうことで謝礼を支払います。つまり、参加者の募集と謝礼の支払いをクラウドソーシングサービスで、実験課題と入力用コードの表示をoTreeで行っているということです。

マッチング不成立問題
しかし、グループで相互作用ををするタイプの実験をするときに、困ることがあります。それは、なかなか参加者のマッチングが成立しない場合があることです。例えば、4人グループで行う実験で最後の一人がなかなか来ないと、先に来た3人は実験課題が始められず、ずっと待つことになってしまうわけです。
その対策として、以前はマッチング待機画面を用意して「5分以上マッチングが成立しない場合は連絡してください、謝礼をお渡しするためのコードを送ります」とお伝えしていたのですが、必要以上に連絡をお願いするのもよくないですし、手動だと安定しないので、自動で謝礼支払い用のコードが表示されるようにしました。

画面見本


※ 静止画だとわからないですが、黒丸部分がアニメーションになっていて、動きます。
方法: 時間が経過したらメッセージを表示するJavescriptのついたWaitページを用意する
matchingWait.html
oTreeで使うテンプレートとして、こちらのコードをmatchingWait.htmlとして追加します。
static→templatesの中に新しいhtmlファイルmatchingWait.htmlを追加します。

{{ block content }}
<div id="wait-text">
    他の参加者が来るのを待っています。5分経っても4人の参加者がマッチングしなかった場合は、謝礼をお渡しするためのコードを自動で表示します。
</div>
<div class="spinner" id="spinner">
    <div class="double-bounce1"></div>
    <div class="double-bounce2"></div>
</div>
<script type="text/javascript">
    function startCountdown() {
        var countdownEnd = localStorage.getItem('countdownEnd');
        if (!countdownEnd) {
            countdownEnd = Date.now() + 5* 60 * 1000; // 5分後のタイムスタンプを保存
            localStorage.setItem('countdownEnd', countdownEnd);
        }
        var interval = setInterval(function() {
            var now = Date.now();
            var timeLeft = countdownEnd - now;
            if (timeLeft <= 0) {
                clearInterval(interval);
                clearInterval(refreshInterval); // リフレッシュを停止
                document.getElementById('wait-text').innerText = '5分以内にマッチングしませんでした。【数字】0000 をクラウドソーシング側にご入力いただければ、謝礼をお支払いします。入力後、こちらの画面は閉じていただいて構いません。この度は誠にありがとうございました。';
                document.getElementById('spinner').style.display = 'none'; // アニメーションを停止
                localStorage.removeItem('countdownEnd');
            }
        }, 1000);
    }
    // ページがリロードされてもタイマーを保持
    startCountdown();
    // 5秒ごとにページをリフレッシュ
    var refreshInterval = setInterval(function() {
        location.reload();
    }, 5000);
</script>
<style>
    .spinner {
        width: 40px;
        height: 40px;
        position: relative;
        margin: 100px auto;
    }
    .double-bounce1, .double-bounce2 {
        width: 100%;
        height: 100%;
        border-radius: 50%;
        background-color: #333;
        opacity: 0.6;
        position: absolute;
        top: 0;
        left: 0;
        -webkit-animation: sk-bounce 2.0s infinite ease-in-out;
        animation: sk-bounce 2.0s infinite ease-in-out;
    }
    .double-bounce2 {
        -webkit-animation-delay: -1.0s;
        animation-delay: -1.0s;
    }
    @-webkit-keyframes sk-bounce {
        0%, 100% { -webkit-transform: scale(0.0) }
        50% { -webkit-transform: scale(1.0) }
    }
    @keyframes sk-bounce {
        0%, 100% { 
            transform: scale(0.0);
            -webkit-transform: scale(0.0);
        } 50% { 
            transform: scale(1.0);
            -webkit-transform: scale(1.0);
        }
    }
</style>
{{ endblock }}
ポイントは、5秒おきにリフレッシュしているところです。これをしないと、マッチングが成立したのにも関わらず画面がこのページのまま自動で切り替わらないという問題が起こりました。
また、<style>~<style>の部分は、待っているときのアニメーションを表示させるためだけのコードなので、別になくてもいいです(上の画面見本の黒い〇の部分です)。
iniy.py Pages部分での設定
作ったテンプレートを読み込むように設定しています。
# PAGES class matchingWait(WaitPage): def is_displayed(player): return player.round_number == 1 template_name = 'matchingWait.html' def after_all_players_arrive(group: Group):
懸念点: サーバーへの負荷
懸念点として、「5秒ごとにリフレッシュする」という動作がどれくらいサーバーに負担をかけるのかがわかっていません。
少なくともこの間の実験では、Herokuサーバーの
dyno: Standard x2
Postgress Standard 7
同時接続: 100人
で、問題はありませんでした。

最近のコメント