クラウドソーシングサービス×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人
で、問題はありませんでした。