// ==UserScript==
// @name Mturk Hourly
// @author @Kerek
// @description Record time spent working on HITs.
// @match https://www.mturk.com/mturk/accept*
// @match https://www.mturk.com/mturk/preview*
// @match https://www.mturk.com/mturk/continue*
// @match https://www.mturk.com/mturk/submit
// @match https://www.mturk.com/mturk/return*
// @match https://www.mturk.com/mturk/statusdetail*
// @match https://www.mturk.com/mturk/dashboard
// @require http://code.jquery.com/jquery-latest.min.js
// @version 1.0
// @grant none
// @namespace https://greasyfork.org/users/11205
// ==/UserScript==
if (window.location.href == "https://www.mturk.com/mturk/dashboard" && $("#total_earnings_amount").length){
Todays_Projected_Earnings();
}
else{
var hit_returned = false;
if(typeof(Storage)!=="undefined")
{
$('img[src="/images/return_hit.gif"]').parent().click(function(){
hit_returned = true;
});
store_data('open');
window.addEventListener('beforeunload', function(){store_data('close');});
var $requesters = $('td[class="statusdetailRequesterColumnValue"]');
if ($requesters.length > 0)
{
$requesters.each(function(){
var hitId = $(this).find('a[href^="/mturk/contact?"]').attr('href').match(/[A-Z0-9]{30}/);
var $feedback_value = $(this).parent().find('td[class="statusdetailStatusColumnValue"]').next();
var feedback_str = create_feedback_str(hitId);
var status_value = $feedback_value.html() + feedback_str;
$feedback_value.html(status_value);
});
}
}
}
function Todays_Projected_Earnings(){
var TPEhitLOG = {}; var TPEdetailsLOG = {}; var TPEhourlyLOG = {}; var pe = 0;
var today = $("a[href^='/mturk/statusdetail?encodedDate']:contains(Today)").eq(0).prop("href");
var $peTR = $('<div id="TPE_div" class="even" style="display:table-row">');
var $peTD1 = $('<td class="metrics-table-first-value">');
var $peTD2 = $('<td>');
var $peA = $('<a href="javascript:void(0)">Today\'s Projected Earnings</a>');
var $TPE_details = $('<span style="color: blue; font-size: 10px; cursor: pointer; float: right;">Details<img style="margin-left: 5px;" src="/media/more.gif" border="0/"></span>');
var $peSPAN = $('<span class="reward">$0.00</span>');
$("td.metrics-table-first-value:contains(Total Earnings)").parent().after($peTR);
$peTR.append($peTD1.append($peA,$TPE_details),$peTD2.append($peSPAN));
var $TPED_table = $('<table style="display: none;" width="760" align="center" cellspacing="0" cellpadding="0">');
var $TPED_tboday = $('<tbody>');
var $TPED_tr_1 = $('<tr id="TPE_tr" height="25px"><td width="10" bgcolor="#7fb4cf" style="padding-left: 10px;"></td><td width="100%" bgcolor="#7fb4cf" class="white_text_14_bold">Today\'s Projected Earnings Details <a id="fourmEXPORT" href="javascript:void(0)" class="whatis" >(Forum Export)</a></td><td width="10" align="right" bgcolor="#7fb4cf"></td></tr>');
var $TPED_tr_2 = $('<tr><td class="container-content" colspan="3"><table class="metrics-table" width="100%"><tbody><tr><td width="100%"><table class="metrics-table" width="100%"><tbody id="tbody2"></tbody></table></td></tr></tbody></table></td></tr>');
var $TPED_tr_h = $('<tr class="metrics-table-header-row"><th class="metrics-table-first-header">Requester</th><th>Submitted</th><th>Projected</th><th>Hourly</th></tr>');
$("#subtabs_and_searchbar").next().next().after($TPED_table);
$TPED_table.append($TPED_tboday);
$TPED_tboday.append($TPED_tr_1,$TPED_tr_2);
$("#tbody2").append($TPED_tr_h);
$("#fourmEXPORT").click(function(){
var exportcode = "";
var bonus = $("#bonus").text();
if (bonus !== "$0.00"){
var total = (Number(pe)+Number(bonus.replace(/[^0-9.]/g, ""))).toFixed(2);
exportcode += "[b]Today's Projected Earnings: $"+Number(pe).toFixed(2)+" + Bonuses: "+bonus+" = $"+total+"[/b]\n";
}
else {
exportcode += "[b]Today's Projected Earnings: $"+Number(pe).toFixed(2)+"[/b]\n";
}
exportcode += "[spoiler=Today's Projected Earnings Full Details][table][tr][th][b]Requester[/b][/th][th][b]Submitted[/b][/th][th][b]Projected[/b][/th][/tr]";
var x_sorted = Object.keys(TPEdetailsLOG).sort(function(a,b){return TPEdetailsLOG[a].reward - TPEdetailsLOG[b].reward;});
for (var j = x_sorted.length-1; j > -1; j--){
var xkey = x_sorted[j];
var x_req = TPEdetailsLOG[xkey].req;
var x_reqid = TPEdetailsLOG[xkey].reqid;
var x_submitted = TPEdetailsLOG[xkey].submit;
var x_reward = Number(TPEdetailsLOG[xkey].reward).toFixed(2);
if (x_req === "Bonuses"){
if (x_reward !== "0.00"){
exportcode += "[tr][td]"+x_req+"[/td][td]"+x_submitted+"[/td][td]$"+x_reward+"[/td][/tr]\n";
}
}
else {
exportcode += "[tr][td][url=https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId="+x_reqid+"]"+x_req+"[/url][/td][td]"+x_submitted+"[/td][td]$"+x_reward+"[/td][/tr]\n";
}
}
exportcode += "[/table][/spoiler]";
GM_setClipboard(exportcode);
alert("Forum Export copied to your clipboard.");
});
$peA.click(function(){
if ($peA.text() === "Today's Projected Earnings"){
var confirmation = confirm("Are you sure you want to recalculate Today's Projected Earnings?");
if (confirmation === true){
TPEhitLOG = {}; TPEdetailsLOG = {}; TPEhourlyLOG = {}; pe = 0;
$("#tbody2").find("tr.odd, tr.even").remove();
getDATA(today);
$peSPAN.text("$0.00");
}
}
});
$TPE_details.click(function(){
$TPE_details.find("img").attr("src", ($TPE_details.find("img").attr("src") === "/media/more.gif") ? "/media/less.gif" : "/media/more.gif");
$TPED_table.toggle();
});
if (today){
var date = today.split("encodedDate=")[1];
if (date === localStorage.TPE_date){
if (localStorage.TPEhitLOG){
TPEhitLOG = JSON.parse(localStorage.TPEhitLOG);
}
pe = Number(localStorage.TPE_pe) || 0;
$peSPAN.text("$"+Number(pe).toFixed(2));
getDATA(localStorage.TPE_lastpage);
}
else {
localStorage.TPE_date = date;
localStorage.Goal_progress = 0;
TPEhitLOG = {}; pe = 0;
$peSPAN.text("$0.00");
getDATA(today);
}
}
function getDATA(URL){
var page = URL.match(/Number=([0-9]*)/g);
if (page){
$peA.text("Calculating Page "+page.toString().replace(/[^0-9.]/g, ""));
}
else {
localStorage.removeItem("TPEhitLOG");
localStorage.Goal_progress = 0;
$peA.text("Calculating Page 1");
}
$.get(URL, function(data){
var $data = $(data);
var $hits = $data.find("#dailyActivityTable").find("tr[valign='top']");
var pagereqerr = $data.find("td.error_title:contains(You have exceeded the maximum allowed page request rate for this website.)").length;
var noactivity = $data.find("#dailyActivityTable").find("td:contains(You have no HIT activity on this day matching the selected status.)").length;
if ($hits.length){
console.log("hit length");
var url = $data.find("a:contains(Next)").eq(0).prop("href");
for (var i = 0; i < $hits.length; i++){
var req = $hits.eq(i).find("td.statusdetailRequesterColumnValue").text().trim();
var title = $hits.eq(i).find("td.statusdetailTitleColumnValue").text().trim();
var reward = $hits.eq(i).find("td.statusdetailAmountColumnValue").text().trim();
var status = $hits.eq(i).find("td.statusdetailStatusColumnValue").text().trim();
var reqid = $hits.eq(i).find("a").prop("href").split("requesterId=")[1].split("&")[0];
var hitid = $hits.eq(i).find("a").prop("href").split("HIT+")[1];
if (!TPEhitLOG[hitid]){
TPEhitLOG[hitid] = {
req : req,
title : title,
reward : reward,
status : status,
reqid : reqid,
hitid : hitid
};
}
}
if (url){
getDATA(url);
}
else {
pe = 0;
for(var key in TPEhitLOG){
if (TPEhitLOG[key].status !== "Rejected"){
pe += parseFloat(TPEhitLOG[key].reward.replace(/[^0-9.]/g, ""));
}
if (!TPEdetailsLOG[TPEhitLOG[key].reqid]){
TPEdetailsLOG[TPEhitLOG[key].reqid] = {
req : TPEhitLOG[key].req,
submit : 1,
reward : parseFloat(TPEhitLOG[key].reward.replace(/[^0-9.]/g, "")),
reqid : TPEhitLOG[key].reqid
};
}
else {
TPEdetailsLOG[TPEhitLOG[key].reqid].submit = TPEdetailsLOG[TPEhitLOG[key].reqid].submit + 1;
TPEdetailsLOG[TPEhitLOG[key].reqid].reward = TPEdetailsLOG[TPEhitLOG[key].reqid].reward + parseFloat(TPEhitLOG[key].reward.replace(/[^0-9.]/g, ""));
}
if (!TPEhourlyLOG[TPEhitLOG[key].reqid]){
var time_data = localStorage.getItem('time_data.' + TPEhitLOG[key].hitid.split('&')[0]);
if (time_data != null){
var starts = time_data.split("$#$")[3].split('?');
var last_start = starts[starts.length-1];
var stops = time_data.split("$#$")[4].split('?');
var last_stop = stops[stops.length-1];
if (last_start.length && last_stop.length){
TPEhourlyLOG[TPEhitLOG[key].reqid] = {
req : TPEhitLOG[key].req,
intervals :[[last_start,last_stop]],
totalReward : parseFloat(time_data.split("$#$")[2]),
};
}
}
}
else{
var time_data = localStorage.getItem('time_data.' + TPEhitLOG[key].hitid.split('&')[0]);
if (time_data != null){
var starts = time_data.split("$#$")[3].split('?');
var last_start = starts[starts.length-1];
var stops = time_data.split("$#$")[4].split('?');
var last_stop = stops[stops.length-1];
if (last_start.length && last_stop.length){
//console.log(TPEhourlyLOG[TPEhitLOG[key].reqid].intervals);
TPEhourlyLOG[TPEhitLOG[key].reqid].intervals.push([last_start,last_stop]);
}
// console.log(TPEhourlyLOG[TPEhitLOG[key].reqid].intervals);
TPEhourlyLOG[TPEhitLOG[key].reqid].totalReward += parseFloat(time_data.split("$#$")[2]);
}
}
}
if (!TPEdetailsLOG.bonuses && $("#bonus").length){
TPEdetailsLOG.bonuses = {
req : "Bonuses",
submit : "N/A",
reward : parseFloat($("#bonus").text().replace(/[^0-9.]/g, "")),
reqid : "N/A"
};
}
else if ($("#bonus").length){
TPEdetailsLOG.bonuses.reward = parseFloat($("#bonus").text().replace(/[^0-9.]/g, ""));
}
var d_sorted = Object.keys(TPEdetailsLOG).sort(function(a,b){return TPEdetailsLOG[a].reward - TPEdetailsLOG[b].reward;});
var oddeven = true;
for (var j = d_sorted.length-1; j > -1; j--){
var dkey = d_sorted[j];
var d_req = TPEdetailsLOG[dkey].req;
var d_submitted = TPEdetailsLOG[dkey].submit;
var d_reward = Number(TPEdetailsLOG[dkey].reward).toFixed(2);
var d_hourly = "N/A";
if (d_req !== "Bonuses" && TPEhourlyLOG[TPEdetailsLOG[dkey].reqid]){
var intervals = TPEhourlyLOG[TPEdetailsLOG[dkey].reqid].intervals;
var d_intervals_sum = 0;
for (i=0;i<intervals.length;i++){
d_intervals_sum += (intervals[i][1]-intervals[i][0]);
}
var d_intervals_avg = d_intervals_sum / intervals.length;
var d_intervals = mergeIntervals(intervals);
d_intervals = combineIntervals(intervals,Math.max(2*60*1000,Math.min(10*d_intervals_avg,15*60*1000)));
console.log(d_req, "padding", Math.min(10*d_intervals_avg,15*60*1000));
var d_time = 0;
for (i=0;i<d_intervals.length; i++){
var s = new Date(parseInt(d_intervals[i][0]));
var f = new Date(parseInt(d_intervals[i][1]));
console.log(d_req, i, s.toLocaleTimeString(),f.toLocaleTimeString());
d_time += (d_intervals[i][1] - d_intervals[i][0])/(1000*60*60);
}
d_hourly = "$" + (TPEhourlyLOG[TPEdetailsLOG[dkey].reqid].totalReward / d_time).toFixed(2);
}
if (oddeven){
oddeven = false;
$("#tbody2").append('<tr class="odd"><td class="metrics-table-first-value">'+d_req+'</td><td>'+d_submitted+'</td><td><span class="reward">$'+d_reward+'</span></td><td><span class="reward">'+d_hourly+'</span></tr>');
}
else {
oddeven = true;
$("#tbody2").append('<tr class="even"><td class="metrics-table-first-value">'+d_req+'</td><td>'+d_submitted+'</td><td><span class="reward">$'+d_reward+'</span><td><span class="reward">'+d_hourly+'</span></td></tr>');
}
}
localStorage.TPEhitLOG = JSON.stringify(TPEhitLOG);
localStorage.TPE_lastpage = URL;
localStorage.TPE_pe = pe;
$peA.text("Today's Projected Earnings");
$peSPAN.text("$"+Number(pe).toFixed(2));
$('#today_total').text("$"+(Number($('#bonus').text().replace('$','')) +Number($('#TPE_div span.reward').text().replace('$',''))).toFixed(2));
document.title = $('#today_total').text();
localStorage.Goal_percent = ((Number(localStorage.TPE_pe)/Number(localStorage.Goal_goal))*100);
localStorage.Goal_progress = Number(pe)-Number(localStorage.Goal_goal);
if ($("#goalDIV").length){
$("#progress").width(Number(localStorage.Goal_percent)+"%");
$("#progressper").text(Number(localStorage.Goal_progress).toFixed(2));
}
Unsynced();
}
}
else if (noactivity){
console.log("no activity");
localStorage.TPE_lastpage = URL;
localStorage.TPE_pe = 0;
localStorage.Goal_progress = 0;
$peA.text("Today's Projected Earnings");
$peSPAN.text("$0.00");
$('#today_total').text("$"+(Number($('#bonus').text().replace('$','')) +Number($('#TPE_div span.reward').text().replace('$',''))).toFixed(2));
}
else if (pagereqerr) {
console.log("set timeout");
setTimeout(function(){ getDATA(URL); }, 2000);
}
});
}
function Unsynced(){
var hitscalced = Object.keys(TPEhitLOG).length;
var submitted = Number($("a[href^='/mturk/statusdetail?encodedDate']:contains(Today)").eq(0).parent().next().text());
if (hitscalced < submitted){
$peSPAN.css({backgroundColor:"red"});
}
else {
$peSPAN.css({backgroundColor:""});
}
}
}
function create_feedback_str(hitId)
{
var time_str = '';
var time_data = localStorage.getItem('time_data.' + hitId);
if (time_data === null)
{
return time_str;
}
var last_start = time_data.split("$#$")[3].split('?');
last_start = new Date(parseInt(last_start[last_start.length - 1]));
var last_finish = time_data.split("$#$")[4].split('?');
last_finish = new Date(parseInt(last_finish[last_finish.length - 1]));
console.log(time_data, last_start, last_finish, time_data.split("$#$")[3].split('?'), time_data.split("$#$")[4].split('?'));
var reward = time_data.split("$#$")[2];
var time_spent = last_finish - last_start;
var h = Math.floor(time_spent/(1000*60*60));
var m = Math.floor((time_spent - h*1000*60*60)/(1000*60));
var s = Math.floor((time_spent - h*1000*60*60 - m*1000*60)/(1000));
// return time_data;
// return time_data.split("$#$")[0] + " - " + time_data.split("$#$")[1] + " - " + time_data.split("$#$")[2] + "<br>" +last_start*1000 + "<br>" + last_finish*1000;
return "Opened: " + last_start.toLocaleTimeString() + "<br>Submitted: " + last_finish.toLocaleTimeString()+ "<br>Time: " + (h.length?pad(h,2) + ":":"") + pad(m,2) + ":" + pad(s,2) + "<br>Hourly: $" + (reward/((last_finish-last_start)/(60*60*1000))).toFixed(2) ;
// return time_data.split("$#$")[0] + " - " + time_data.split("$#$")[1] + " - " + time_data.split("$#$")[2] + "<br>" + (new Date(last_start)).toTimeString() + "<br>" + (new Date(last_finish)).toTimeString();
}
function store_data(action_type)
{
var $isAccepted = $('input[type="hidden"][name="isAccepted"][value="true"]');
if ($isAccepted.length > 0 && !hit_returned)
{
var hitReview_hitId = $('form[name="hitForm"][action="/mturk/hitReview"] input[name="hitId"]').val();
console.log(hitReview_hitId);
var hit_reward = $('form[name="hitForm"][action="/mturk/submit"] input[name="prevReward"]').val().replace('USD','');
console.log(hit_reward);
var requester_name = $('td.capsule_field_title:contains("Requester:"):eq(0)').next().text().trim();
var hit_title = $('table:contains("Requester:"):eq(0) table:eq(0)').text().trim();
var now_in_milliseconds = new Date().getTime();
var open_list = "";
var close_list = "";
var stored_copy = localStorage.getItem('time_data.' + hitReview_hitId);
if (stored_copy !== null){
open_list = stored_copy.split('$#$')[3];
close_list = stored_copy.split('$#$')[4];
}
if (action_type == "open"){
open_list += "?" + now_in_milliseconds;
}
else{
close_list += "?" + now_in_milliseconds;
}
var autoapprove_data = requester_name + "$#$" + hit_title + "$#$"+ hit_reward +"$#$" + open_list + "$#$" + close_list;
console.log(autoapprove_data);
localStorage.setItem('time_data.' + hitReview_hitId, autoapprove_data);
}
}
function mergeIntervals(intervals) {
// test if there are at least 2 intervals
if(intervals.length <= 1)
return intervals;
var stack = [];
var top = null;
// sort the intervals based on their start values
intervals = intervals.sort(function (startValue, endValue) {
if (startValue[0] > endValue[0]) {
return 1;
}
if (startValue[0] < endValue[0]) {
return -1;
}
return 0;
});
// push the 1st interval into the stack
stack.push(intervals[0]);
// start from the next interval and merge if needed
for (var i = 1; i < intervals.length; i++) {
// get the top element
top = stack[stack.length - 1];
// if the current interval doesn't overlap with the
// stack top element, push it to the stack
if (top[1] < intervals[i][0]) {
stack.push(intervals[i]);
}
// otherwise update the end value of the top element
// if end of current interval is higher
else if (top[1] < intervals[i][1])
{
top[1] = intervals[i][1];
stack.pop();
stack.push(top);
}
}
return stack;
}
function combineIntervals(intervals, padding) {
// test if there are at least 2 intervals
if(intervals.length <= 1)
return intervals;
var stack = [];
var top = null;
// push the 1st interval into the stack
stack.push(intervals[0]);
// start from the next interval and merge if needed
for (var i = 1; i < intervals.length; i++) {
// get the top element
top = stack[stack.length - 1];
// if the current interval doesn't overlap with the
// stack top element, push it to the stack
if ((intervals[i][0]-top[1]) > padding) {
// console.log("hi",(intervals[i][0]-top[1]) + padding);
stack.push(intervals[i]);
}
// otherwise update the end value of the top element
// if end of current interval is higher
else if (top[1] < intervals[i][1])
{
top[1] = intervals[i][1];
stack.pop();
stack.push(top);
}
}
//console.log("stack",stack);
return stack;
}
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}