Worker-MTurk Qual Sorter (with fix)

Keep Track Of Qualifications And Create A More Sortable List.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              Worker-MTurk Qual Sorter (with fix)
// @author            Eisenpower
// @namespace         Uchiha Clan
// @version           0.531
// @icon              https://i.imgur.com/M0jWVYS.png
// @description       Keep Track Of Qualifications And Create A More Sortable List.
// @include           https://worker.mturk.com/dashboard*
// @include           https://worker.mturk.com/qualtable*
// @require           https://code.jquery.com/jquery-3.3.1.min.js
// @require           https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require           https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.1/js/jquery.dataTables.js
// @resource       fa https://ddvx2zdcut4vw.cloudfront.net/assets/application-c6bbd5228d4b2574a79e8828bf98dfe4b2f37ab3cc3052173436ac49f2b434f0.css
// @resource  datatab https://greasyfork.org/scripts/4258-datatables-css-cdn-with-images/code/Datatables%20CSS%20CDN%20with%20Images.js?version=13654
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_addStyle
// @grant             GM_getResourceText
// ==/UserScript==
 
// Original Code By 'DeliriumTremens 2014' - Patched And Revamped
 
//
// GET CSS MODULES
//
 
GM_addStyle(GM_getResourceText("datatab"));
GM_addStyle(GM_getResourceText("fa"));
 
//
// END CSS MODULES
//
// ******************************************************************
//
//	START VARIABLE DEFINITIONS
//
 
var qualCount = null; // hold onto that sweet, sweet qual count
var qualPrev = GM_getValue("quals"); // store the previous qual count
var qualDiff = 0; // difference in qual count between dashboard refreshes
var nextPage = "https://worker.mturk.com/qualifications/assigned.json?page_size=100"; // Starting Page of Scrape
var qualObject = {}; // Storage for qualification details to be sent to database
var QualStorage = {}; // QualStorage object definition
var Scraping = true; // Used to start and kill scraping
var scrapeNumber = 0; // Unused currently
var currScrape = null; // Container for current page being scraped
var skipQuals = ['Exc: ', 'Qualification_', 'SurveyGroup ', 'TP Panel:', 'Inc: ', 'campaign_'];
 
//
//	END VARIABLE DEFINITIONS
//
// ******************************************************************
//
//	START INDEXEDDB METHODS
//
 
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange;
var idbKeyRange = window.IDBKeyRange;
QualStorage.indexedDB = {};
QualStorage.indexedDB.db = null;
var v = 1; // Database version.
var dbExists = true; // Boolean if database exists.
 
// Method for creating the new database.
QualStorage.indexedDB.create = function () {
    var request = indexedDB.open("QualDB", v);
    request.onupgradeneeded = function (e) { QualStorage.indexedDB.upgrade(e) };
    request.onsuccess = function (e) {
        QualStorage.indexedDB.db = e.target.result;
        var db = QualStorage.indexedDB.db;
        db.close();
    };
    //request.onerror = console.log(request.errorCode);
};
 
QualStorage.indexedDB.upgrade = function (e) {
    QualStorage.indexedDB.db = e.target.result;
    var db = QualStorage.indexedDB.db;
    var newDB = false;
    if(!db.objectStoreNames.contains("Quals")) {
        var store = db.createObjectStore("Quals", {
            keyPath: "qualId"
        });
        store.createIndex("qualName", "qualName", {
            unique: false
        });
        store.createIndex("author", "author", {
            unique: false
        });
        store.createIndex("desc", "desc", {
            unique: false
        });
        store.createIndex("assDate", "assDate", {
            unique: false
        });
        store.createIndex("retake", "retake", {
            unique: false
        });
        store.createIndex("hasTest", "hasTest", {
            unique: false
        });
        store.createIndex("value", "value", {
            unique: false
        });
        newDB = true;
    }
    db.close();
}
 
QualStorage.indexedDB.addQual = function (qual) {
    var request = indexedDB.open("QualDB", v);
    var qualPut = qual;
    request.onsuccess = function (e) {
        QualStorage.indexedDB.db = e.target.result;
 
        var db = QualStorage.indexedDB.db;
        var newDB = false;
 
        if (!db.objectStoreNames.contains("Quals")) {
            db.close();
        } else {
            var trans = db.transaction(["Quals"], 'readwrite');
            var store = trans.objectStore("Quals");
            var request;
 
            request = store.put({
                qualId: qualPut["qualId"],
                qualName: qualPut["qualName"],
                author: qualPut["author"],
                desc: qualPut["desc"],
                assDate: qualPut["assDate"],
                retake: qualPut["retake"],
                hasTest: qualPut["hasTest"],
                value: qualPut["value"]
            });
            request.onsuccess = function (e) {
            };
            request.onerror = function (e) {
            };
        }
        db.close();
    };
    request.onerror = QualStorage.indexedDB.onerror;
};
 
QualStorage.indexedDB.getQuals = function (e) {
    var request = indexedDB.open("QualDB", v);
 
    request.onupgradeneeded = function (e) {
        QualStorage.indexedDB.upgrade(e);
        QualStorage.indexedDB.getQuals();
    };
 
    request.onsuccess = function (e) {
        if (e.target.result.objectStoreNames.length === 0) {
            e.target.result.close();
            var DBDeleteRequest = indexedDB.deleteDatabase('QualDB')
            DBDeleteRequest.onsuccess = function (e) {
                return QualStorage.indexedDB.getQuals();
            }
        }
 
        QualStorage.indexedDB.db = e.target.result;
 
        var db = QualStorage.indexedDB.db;
        var transaction = db.transaction('Quals', 'readonly');
        var store = transaction.objectStore('Quals');
 
        var results = [];
        var tmp_results = {};
 
        store.openCursor().onsuccess = function (event) {
            var cursor = event.target.result;
            if (cursor) {
                var qual = cursor.value;
                if (tmp_results[cursor.key] === undefined) {
                    tmp_results[cursor.key] = [];
                    tmp_results[cursor.key][0] = qual.qualId;
                    tmp_results[cursor.key][1] = qual.qualName;
                    tmp_results[cursor.key][2] = qual.author;
                    tmp_results[cursor.key][3] = qual.desc;
                    tmp_results[cursor.key][4] = qual.assDate;
                    tmp_results[cursor.key][5] = qual.retake;
                    tmp_results[cursor.key][6] = qual.hasTest;
                    tmp_results[cursor.key][7] = qual.value;
                }
                cursor.continue();
            } else {
                for (var key in tmp_results) {
                    results.push(tmp_results[key]);
                }
                buildQualTable(results);
            }
        };
        db.close();
    };
};
 
//
//	END INDEXEDDB METHODS
//
// ******************************************************************
//
//	START SCRAPER METHODS
//
 
const scrapeQuals = function (page) {
    QualStorage.indexedDB.create();
    $.get(page, function(data) {
        const quals = data.assigned_qualifications;
 
        for (var i = 0; i < quals.length; i++) {
            qualObject = {
                "qualId": quals[i].retake_test_url.match(/[A-Z0-9]{15,}/)[0],
                "qualName": quals[i].name,
                "author": quals[i].creator_name,
                "desc": quals[i].description,
                "assDate": quals[i].grant_time,
                "retake": quals[i].can_retake_test_or_rerequest ? "Yes" : "No",
                "hasTest": quals[i].has_test ? quals[i].retake_test_url : "No",
                "value": quals[i].value
            };
            QualStorage.indexedDB.addQual(qualObject);
        }
 
        if (data.next_page_token) {
            const next = `https://worker.mturk.com/qualifications/assigned.json?page_size=100&next_token=${encodeURIComponent(data.next_page_token)}`
            setTimeout(() => scrapeQuals(next), 3000);
        }
 
        else {
            if (window.location.href === 'https://worker.mturk.com/qualtable') window.location.reload();
            $('.updatePercentage').css('visibility', 'visible');
            $('.updatePercentage').css('color', 'darkseagreen');
            $('.updateLink > .fa-refresh')[0].classList.remove("rotate");
        }
    });
};
 
//
//	END SCRAPER METHODS
//
// ******************************************************************
//
// START EVENT HANDLERS
//
 
const addQualElement = function () {
    $('.expand-collapse-projects-holder').prepend('<div class="qualSorterContainer" style="line-height: 1; white-space: nowrap; margin-right: 20px;"></div>');
    $('.qualSorterContainer').prepend('<span>Qual Sorter:</span>');
    $('.qualSorterContainer').append('<div class="qualSorterButtonContainer" style="font-size: large"></div>');
    $('.qualSorterButtonContainer').append('<a href="#" title="View Quals" class="qualTableBut" style="margin-left: 12px"><i class="fa fa-columns"></i></a>')
    $('.qualSorterButtonContainer').append('<a href="#" title="Update Quals" class="updateLink" style="margin-left: 8px"><i class="fa fa-refresh"></i></a>');
    $('.qualSorterButtonContainer').append('<span class="updatePercentage" style="font-size: small; visibility: hidden;"><i class="fa fa-check-circle-o"></i></span>')
};
 
const buildQualTable = function (qualStore) {
    $('#qualTable').append('<thead><tr><th>Qual ID</th><th>Qual Name</th><th>Author</th><th>Description</th><th width="75px" style="text-align: center">Assgn</th><th style="text-align: center">Retake Available</th><th style="text-align: center">Has Test</th><th style="text-align: center">Value</th></tr></thead>');
    $('#qualTable').append('<tbody></tbody>');
    for (var j = 0; j < qualStore.length; j++) {
        if (!skipQuals.some(function(v) { return qualStore[j][1].indexOf(v) >= 0; })) {
            var qualRow = String(".qualRow" + j);
 
            var tr = document.createElement("tr");
            tr.setAttribute('class', ('qualRow' + j));
 
            $(tr).append(`<td>${qualStore[j][0]}</td>`);
            $(tr).append(`<td>${qualStore[j][1]}</td>`);
            $(tr).append(`<td>${qualStore[j][2]}</td>`);
            $(tr).append(`<td style='width:350px'>${qualStore[j][3]}</td>`);
            $(tr).append(`<td>${qualStore[j][4]}</td>`);
            $(tr).append(`<td style='text-align: center'>${qualStore[j][5]}</td>`);
            $(tr).append(`<td style='text-align: center'>${qualStore[j][6] == "No" ? "No" : `<a href=${qualStore[j][6]}>TEST</a>`} </td>`);
            $(tr).append(`<td style='text-align: center'>${qualStore[j][7]}</td>`);
 
            $('#qualTable tbody').append(tr);
        }
    }
    $('#qualTable').dataTable({
        paging: false
    });
    $('#qualTable_wrapper').prepend(`<a href="#" title="Update Quals" class="updateLink" style="margin-left: 8px; font-size: x-large; color: #146EB4; vertical-align: -webkit-baseline-middle;"><i class="fa fa-refresh"></i></a>`);
    $('#qualTable_wrapper').prepend(`<div style="float: left; padding: 5px;"><b>Total Quals: </b>${qualStore.length.toLocaleString()}</div>`);
};
 
$(document).on('click', '.updateLink', function (e) {
    e.preventDefault();
    $('.updateLink > .fa-refresh')[0].classList.add("rotate");
    $('.updatePercentage').css('visibility', 'hidden');
    $('.updatePercentage').css('color', '');
    scrapeQuals(nextPage);
});
 
$(document).ready( function () {
    if (document.URL.includes("https://worker.mturk.com/qualtable")) {
        $('body').html('');
        var qualTable = document.createElement("table");
        qualTable.setAttribute('class', 'display');
        qualTable.setAttribute('id', 'qualTable');
        $('body').append(qualTable);
 
        var qualElements = QualStorage.indexedDB.getQuals();
    }
});
 
$(document).on('click', '.qualTableBut', function () {
    window.open("https://worker.mturk.com/qualtable", '_blank');
});
 
//
//	END EVENT HANDLERS
//
// ******************************************************************
//
//  START STYLING
//
 
const styles = function() {
  var styleEm = document.createElement('style');
  document.head.appendChild(styleEm);
  var styleSheet = styleEm.sheet;
 
  var rule1 = `@-webkit-keyframes rotate {
                    from{
                        -webkit-transform: rotate(0deg);
                    }
                    to{
                        -webkit-transform: rotate(360deg);
                    }
                }`
 
  var rule2 = `.rotate {
                    -webkit-animation: rotate 1s linear infinite;
                }`
 
    styleSheet.insertRule(rule1, styleSheet.cssRules.length);
    styleSheet.insertRule(rule2, styleSheet.cssRules.length);
}
 
//
//  END STYLING
//
 
addQualElement();
styles();