Mturk Hourly

Record time spent working on HITs.

目前為 2017-07-10 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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&nbsp;&nbsp;<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;
}