Greasy Fork

MTurk Wage Reporter

Tracks a best-estimate hourly wage on active HITs being worked.

当前为 2014-07-15 提交的版本,查看 最新版本

// ==UserScript==
// @name                        MTurk Wage Reporter
// @namespace                   localhost
// @description                 Tracks a best-estimate hourly wage on active HITs being worked.
// @include                     https://www.mturk.com/mturk/accept?*
// @include                     https://www.mturk.com/mturk/continue?*
// @include                     https://www.mturk.com/mturk/previewandaccept?*
// @include                     https://www.mturk.com/mturk/dashboard*
// @include                     https://www.mturk.com/mturk/submit*
// @version                     0.4.1b
// @grant                       GM_setValue
// @grant                       GM_getValue
// @require                     http://code.jquery.com/jquery-2.1.1.js
// @downloadURL
// @updateURL
// @author                      DeliriumTremens 2014
// ==/UserScript==
 
//
// 2014-07-10   0.1b    Beginning development.  Creating timer and tab tracker, as well as initial IndexedDB for storage of data.
//
 
// ------------------------------------------------------------------------------------------------------------------------------------------------
 
// First, create indexedDB variables.
// This sets up the various indexedDB functions and processes for interacting with WageDB.
// Parts borrowed from HITdb
var indexedDB = window.indexedDB || window.webkidIndexedDB || window.mozIndexedDB;
 
var WageStorage = {};
 
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange;
 
WageStorage.IDBTransactionModes = { "READ_ONLY": "readonly", "READ_WRITE": "readwrite", "VERSION_CHANGE": "versionchange" };
 
var idbKeyRange = window.IDBKeyRange;
 
WageStorage.indexedDB = {};
WageStorage.indexedDB.db = null;
 
// Global catch for indexedDB errors
WageStorage.indexedDB.onerror = function(e) {
        console.log(e);
}
 
// Check if database exists.  If no database, create one.  
// Parts borrowed from HITdb.
var inProgress = false; // Boolean for when a HIT is continued from queue
var dbExists = true; // Boolean for when the database is already created
var v = 5; // Version of the database
 
WageStorage.indexedDB.create = function () { // Function that creates the database if it hasn't been created already.
        var request = indexedDB.open("WageDB", v);
       
        request.onupgradeneeded = function (e) {
                WageStorage.indexedDB.db = e.target.result;
               
                var db = WageStorage.indexedDB.db;
                var newDB = false;
               
                if(!db.objectStoreNames.contains("Wage")) {
                        var store = db.createObjectStore("Wage", { keyPath: "hitId" });  // Primary key of the database (not an index)
                       
                        store.createIndex("date", "date", {unique: false});  // These are the other fields (or Indexes) of the database.
                        store.createIndex("reqName", "reqName", {unique: false});
                        store.createIndex("reqId", "reqId", {unique: false});
                        store.createIndex("reward", "reward", {unique: false});
                        store.createIndex("start", "start", {unique: false});
                        store.createIndex("stop", "stop", {unique: false});
            			store.createIndex("queue", "queue", {unique: false});
                       
                        newDB = true;  
                }      
        db.close();
        }
        request.onsuccess = function(e) {
                WageStorage.indexedDB.db = e.target.result;
                var db = WageStorage.indexedDB.db;
                db.close();
        }
        request.onerror = WageStorage.indexedDB.onerror;
}
 
// Function for adding HIT data into database.  
// Includes logic for updating HITs which were worked on in several sittings (or from queue)
WageStorage.indexedDB.addhit = function () {
        var request = indexedDB.open("WageDB", v);
       
        request.onsuccess = function (e) {
                WageStorage.indexedDB.db = e.target.result;
               
                var db = WageStorage.indexedDB.db;
                var newDB = false;
               
                if(!db.objectStoreNames.contains("Wage")) { // Make sure database is there... This should never fire.
                        db.close();
                }
                else {
                        var trans = db.transaction(["Wage"],WageStorage.IDBTransactionModes.READ_WRITE);
                        var store = trans.objectStore("Wage");
           
                        var request;
            			request = store.put({ hitId: hitId, date: date, reqName: reqName, reqId: reqId[1], reward: reward, start: wageStart, stop: wageEnd, queue: wageExtra }); // Insert fresh hit into database
                }      
        db.close();
        }
        request.onerror = WageStorage.indexedDB.onerror;
}
 
// Function for getting todays data.
// Gets reward amount, start time, stop time, and queue time (unworked time)
WageStorage.indexedDB.getWage = function () {
    var request = indexedDB.open("WageDB", v);
   
        request.onsuccess = function (e) {
        WageStorage.indexedDB.db = e.target.result;
       
        var db = WageStorage.indexedDB.db
        var transaction = db.transaction('Wage','readonly');
        var store = transaction.objectStore('Wage');
        var index = store.index('date');
        var range = IDBKeyRange.only(date);
       
        var results = [];
        var tmp_results = {};
       
        index.openCursor(range).onsuccess = function(event) {
            var cursor = event.target.result;
                if (cursor) {
                    var hit = cursor.value;
                    if (tmp_results[hit.hitId] === undefined) {
                        tmp_results[hit.hitId] = [];
                        tmp_results[hit.hitId][0] = hit.reward;
                        tmp_results[hit.hitId][1] = hit.start;
                        tmp_results[hit.hitId][2] = hit.stop;
                    }
                    cursor.continue();
                }
                else {
                    for (var key in tmp_results) {
                        results.push(tmp_results[key]);
                    }
                    addTableElement(results); // Calls function to add wage to dashboard after all data has been pulled
                }
        }
        }
}
 
// Function to check if HIT has already been started
WageStorage.indexedDB.getHit = function () {
    var request = indexedDB.open("WageDB", v);
   
        request.onsuccess = function (e) {
        	WageStorage.indexedDB.db = e.target.result;
       
        	var hitId2 = $('input[name="hitId"]').val();
       
        	var db = WageStorage.indexedDB.db
        	var transaction = db.transaction('Wage','readonly');
        	var store = transaction.objectStore('Wage');
       
        	var hitCheck = store.get(hitId2);
        	hitCheck.onsuccess = function (event) {
                if (event.target.result === undefined) {
                    inProgress = true;
                }
                else {
            		inProgress = false;
                }
        	}
        }
}
 
// Script Variables
var wageLost = false; // Boolean for a returned hit, and a wage that is abandoned.  Will record the time with zero payment.
var wageExtra = 0; // Placeholder for extra time that was not worked.
var wageStart = null; // Placeholder for start time of HIT
var wageEnd = null; // Placeholder for end time of HIT
var reqId = document.URL.match(/requesterId=(.*?)&/i); // Parse requester ID out of URL
 
if (reqId) {
        GM_setValue("reqId", reqId); // If no requester ID is available, it's because you accepted next hit in batch. Retrieves ID.
        }
else {
        reqId = GM_getValue("reqId"); // Stores requester ID in a global script var to be pulled in case of batch work.
        }
 
// HIT Data
var hitId = $('input[name="hitId"]').val(); // Parses hit id from html
var reward = parseFloat($('span[class="reward"]:eq(1)').text().replace('$','')); // Parses reward from html
var reqName = $('input[name="prevRequester"]').val(); // Parses requester name from html
var date = new Date(); // Get todays date
date = date.toLocaleDateString().replace(/\//g,'-'); // Convert date to usable string
 
// Create table element for showing hourly wage.  Parts borrowed from Today's Projected Earnings script
var allTds, thisTd; 
var allTdhtml = [];
 
addTableElement = function (wage) { // Function to add the table cells to dashboard and displaying the wage
    var currStart = 0;
    var currEnd = 0;
    var currWages = [];
    var currWage = 0;
   
    for (var i = 0; i < wage.length; i++) {
        currStart = ((wage[i][1] > currStart) ? wage[i][1] : currStart);
        currEnd = ((wage[i][2] > currEnd) ? wage[i][2] : currEnd);
        currWages.push(((((currEnd - currStart) / 1000) / 3600) * 10000) * wage[i][0]);
    }
   
    for (var i = 0; i < currWages.length; i++) {
        currWage += currWages[i];
    }
   
    currWage = parseFloat(currWage/currWages.length).toFixed(2);
   
    var belowThisTD = (($.inArray('<a href=\"https://www.mturk.com/mturk/dashboard\">Today\'s Projected Earnings</a>',allTdhtml) > -1) ? /Today's Projected Earnings/ : /Total Earnings/ ); // If Projected Earnings script is installed, this will be sure to place is in the correct spot
    var rowColor = (($.inArray('<a href=\"https://www.mturk.com/mturk/dashboard\">Today\'s Projected Earnings</a>',allTdhtml) > -1) ? "#f1f3eb" : "FFFFFF" ); // If Projected Earnings script is installed, this will ensure the correct color is used for the new row
   
    for (var i = 0; i < allTds.length; i++)
        {
            thisTd = allTds[i];
                if ( thisTd.innerHTML.match(belowThisTD) && thisTd.className.match(/metrics\-table\-first\-value/) )
                {
                    var row = document.createElement('tr');
                        row.className = "even";
                        row.setAttribute("style",("background-color:" + rowColor));
 
                        var hourlyWageTitle = document.createElement('p');
                        hourlyWageTitle.innerHTML = "Today's Hourly Wage";
                        hourlyWageTitle.setAttribute("style",("background-color:" + rowColor));
 
                        var cellLeft = document.createElement('td');
                        cellLeft.className = "metrics-table-first-value";
                        cellLeft.appendChild(hourlyWageTitle);
                        row.appendChild(cellLeft);
                       
                        var cellRight = document.createElement('td');
                        cellRight.innerHTML = "$" + currWage + "/hr";
                        row.appendChild(cellRight);
                       
                        thisTd.parentNode.parentNode.insertBefore(row,thisTd.parentNode.nextSibling);
                }
        }
}
 
// Timestamp when the HIT is accepted
$(window).ready( function() {
    if (document.URL === "https://www.mturk.com/mturk/dashboard" ) {} // Don't record anything on dashboard, instead add appropriate element to tables
        else {
                wageStart = new Date().getTime(); // As soon as the window can run something, set the start time.
        }
});
 
$(window).on('load', function() {
    allTds = document.getElementsByTagName('td'); // As soon as the page is fully loaded, grab all of the td elements to an array
    for (var i = 0; i < allTds.length; i++) {
        allTdhtml.push(allTds[i].innerHTML);
    }
    WageStorage.indexedDB.getWage(); // As soon as the page is fully loaded, call function to get todays data from DB
    WageStorage.indexedDB.getHit(); // As soon as the apge is fully loaded, call function to check if HIT is already in DB
});
 
// Detect if 'Return HIT' button has been clicked to record timer as zero-earnings.
// Timestamp when button was clicked to end time spent.
$('a[href*="/mturk/return?"]').on('click', function() {
        wageLost = true;
        wageEnd = new Date().getTime();
});
 
$('img[name="/submit"]').on('click', function() {
        wageLost = false;
});
 
// Create database if not created yet
WageStorage.indexedDB.create();
 
// Detect the page unloading and react accordingly.  
// If return button was clicked, record zero wage earnings, otherwise record earnings.
// Timestamp if submitted to end time spent.
$(window).on('beforeunload', function() {
        if (wageLost) {
                // ***** DO STUFF WHEN RETURN CLICKED *****
                reward = 0;
                WageStorage.indexedDB.addhit();
        }
        else {
                // ***** DO STUFF WHEN SUBMITTED *****
        console.log("This should fire no matter what, unless returned...");
                wageEnd = new Date().getTime();
                WageStorage.indexedDB.addhit();
        }
});