MTurk Goals

Add goals and progess bars to the mTurk Dashboard and Status pages.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         MTurk Goals
// @namespace    http://greasyfork.org/
// @version      0.7
// @description  Add goals and progess bars to the mTurk Dashboard and Status pages.
// @author       Jacob Valenta
// @include      https://www.mturk.com/mturk/dashboard
// @include      https://www.mturk.com/mturk/status
// @grant        none
// ==/UserScript==

var getJsonFromUrl = function(url){
    // Get GET parameters from a url as JSON
    // http://stackoverflow.com/a/8486146
    var regex = /[?&]([^=#]+)=([^&#]*)/g,
        url = url,
        params = {},
        match;
    while(match = regex.exec(url)) {
        params[match[1]] = match[2];
    }
    return params;
};

var setGoal = function(newGoal){
    localStorage.setItem("mturk-goal", parseFloat(newGoal).toFixed(2).toString());
};

var getGoal = function(){
    if (localStorage.getItem("mturk-goal") === null) {
        setGoal(10.00);
        return 10.00;
    }else{
        return parseFloat(localStorage.getItem("mturk-goal"));
    }
};

var percentColors = [
    { pct: 0.0, color: { r: 0xc0, g: 0x50, b: 0x50 } },
    { pct: 0.5, color: { r: 0xc0, g: 0xc0, b: 0x50 } },
    { pct: 1.0, color: { r: 0x50, g: 0xc0, b: 0x50 } } ];

var getColorForPercentage = function(pct) {
    for (var i = 1; i < percentColors.length - 1; i++) {
        if (pct < percentColors[i].pct) {
            break;
        }
    }
    var lower = percentColors[i - 1];
    var upper = percentColors[i];
    var range = upper.pct - lower.pct;
    var rangePct = (pct - lower.pct) / range;
    var pctLower = 1 - rangePct;
    var pctUpper = rangePct;
    var color = {
        r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
        g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
        b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
    };
    return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
};

var getPendingHitsForDate = function(date, pages, callback, prog_container, earned) {
    // Hastily written. I will work on refactoring this.

    // I gave up on javascript's Dates and UTC vs PDT and PST
    // Screw that, just parse the date out of amazon's url...
	var currentPage = 1;

    while (currentPage <= pages){
		var request = new XMLHttpRequest();

        request.onreadystatechange = function() {
            var requestedDate = this._date;

            if (request.readyState == 4 && request.status == 200) {
                var response = request.responseXML;
                var pending = 0.00;

                var hits = response.getElementById("dailyActivityTable").querySelectorAll("tr");
                for (var i = 1; i <= hits.length - 1; ++i) {
                    row = hits[i];

                    var value_column = row.querySelectorAll("td")[2];

                    if (value_column != undefined){
                        var amount = Math.round(100 * parseFloat(value_column.querySelector("span").innerText.replace('$', ''))) / 100;
                        pending += amount;
                    }

                }
                callback(pending, prog_container, earned);
            }
        };

		request.open("GET", "https://www.mturk.com/mturk/statusdetail?encodedDate=" + date + "&sortType=Pending&pageNumber=" + currentPage, true);
        request._date = date;
        request.responseType = "document";
		request.send();

        currentPage += 1;
	}
};

(function() {
    'use strict';

    var page, width, metrics, goal;
    if (location.pathname == "/mturk/dashboard"){
        page = "dashboard";
        width = "200";
    }
    else if (location.pathname == "/mturk/status"){
        page = "status";
        width = "100";
    }

    goal = getGoal();

    if (page == "dashboard"){
        metrics = document.querySelectorAll(".metrics-table")[3].querySelectorAll("tr");

        // Add a change goal button to the bottom of the Dashboard
        var change_goal = document.createElement("td");
        change_goal.innerHTML = 'Goal: <input id="goal" style="width:50px;" value="'+goal.toFixed(2)+'" />&nbsp;<input id="goal-button" type="button" value="Set" style="border: 2px outset buttonface; background-color: buttonface" />';
        var last_td = metrics[metrics.length - 1].querySelectorAll("td")[0];
        last_td.setAttribute("colspan", "9");

        metrics[metrics.length -1].insertBefore(change_goal, last_td);

        // Add click callback
        document.getElementById("goal-button").addEventListener("click", function(){
            // Set goal from input and refresh page
            setGoal(document.getElementById("goal").value);
            location.reload();
        });
    }
    else if (page == "status"){
        metrics = document.querySelectorAll('table')[6].querySelectorAll("tr");
    }

    for (var i = 1; i < metrics.length - 1; ++i) {
        var row = metrics[i];
        var earned = parseFloat(row.querySelectorAll("td:last-child")[0].innerText.replace('$', ''));

        var fraction = (earned / goal);

        var prog = fraction * width;

        var progress_container = document.createElement('div');
        var progress = document.createElement('div');
        var progress_label = document.createElement('div');

        // TODO: Add actual CSS and classes to DOM.
        progress_container.setAttribute("style", "display: inline-block; float: right; width: " + width + "px; height: 6px; background-color: #eee; border: 1px solid #888; margin-top: 3px; border-radius: 1px");
        progress.setAttribute("style", "width: " + prog + "px; max-width: "+width+"px; height: 6px; background-color: "+ getColorForPercentage(fraction) + ";");      

        progress_label.setAttribute("style", "float: right; display: inline-block;");
        progress_label.innerHTML = (Math.round((fraction * 100) * 10) / 10).toFixed(1).toString() + "%&nbsp;&nbsp;";

        progress_container.appendChild(progress);

        row.querySelectorAll("td")[0].appendChild(progress_label).appendChild(progress_container);

        // Get pending hits and add another progress bar
        var dateString = getJsonFromUrl(row.querySelector('td a').getAttribute("href"));
        var pendingHits = parseInt(row.querySelectorAll("td")[4].innerText);
        var pages = Math.ceil(pendingHits / 25);
        if (pendingHits > 0){
            getPendingHitsForDate(dateString.encodedDate, pages, function(amount, container, accepted){
                var pending_earned = accepted + amount;
                var pending_fraction = (pending_earned / goal);
                var pending_prog = pending_fraction * width;

                var pending_progress = document.createElement('div');
                pending_progress.setAttribute("style", "margin-top: -6px; max-width:"+width+"px; height: 6px; opacity: 0.5");
                pending_progress.style.width = pending_prog;
                pending_progress.style["background-color"] = getColorForPercentage(pending_fraction);

               container.appendChild(pending_progress);
            }, progress_container, earned);
        }
    }
})();