您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Brings many enhancements to the MTurk Worker Dashboard.
- // ==UserScript==
- // @name [MTurk Worker] Dashboard Enhancer
- // @namespace http://kadauchi.com/
- // @version 2.2.0
- // @description Brings many enhancements to the MTurk Worker Dashboard.
- // @author Kadauchi
- // @icon http://i.imgur.com/oGRQwPN.png
- // @include https://worker.mturk.com/dashboard*
- // ==/UserScript==
- try { if (mturksuite) return; } catch (e) {}
- const toNum = (string) => Number(string.replace(/[^0-9.]/g, ``));
- const toDate = (string) => string.split(`T`)[0];
- const toMoney = (number) => `$${number.toFixed(2)}`;
- const needPlus = (number) => number > 0 ? `+` : ``;
- const statusDetailsTable = document.querySelector(
- `div[data-react-class="require('reactComponents/dailyWorkerStatisticsTable/DailyWorkerStatisticsTable')['default']"]`,
- );
- const statusDetailsArr = JSON.parse(statusDetailsTable.dataset.reactProps).bodyData;
- const statusDetailsObj = statusDetailsArr.reduce((obj, details) => ({ ...obj, [toDate(details.date)]: details }), {});
- const hitsOverviewTable = document.getElementById(`dashboard-hits-overview`);
- const hitsOverviewRows = hitsOverviewTable.querySelectorAll(`.col-xs-5.text-xs-right`);
- const hitsOverview = {
- approved: toNum(hitsOverviewRows[0].textContent),
- pending: toNum(hitsOverviewRows[1].textContent),
- rejected: toNum(hitsOverviewRows[2].textContent),
- };
- function allApprovedRate() {
- const row = document.createElement(`div`);
- row.className = `row m-b-sm`;
- const col1 = document.createElement(`div`);
- col1.className = `col-xs-7`;
- row.appendChild(col1);
- const strong = document.createElement(`strong`);
- strong.textContent = `All Approved Rate`;
- col1.appendChild(strong);
- const col2 = document.createElement(`div`);
- col2.className = `col-xs-5 text-xs-right`;
- col2.textContent = `${(
- ((hitsOverview.approved + hitsOverview.pending) /
- (hitsOverview.approved + hitsOverview.pending + hitsOverview.rejected)) *
- 100
- ).toFixed(4)}%`;
- row.appendChild(col2);
- const hr = document.getElementById(`dashboard-hits-overview`).getElementsByTagName(`hr`)[1];
- hr.parentNode.insertBefore(row, hr);
- }
- function allRejectedRate() {
- const row = document.createElement(`div`);
- row.className = `row m-b-sm`;
- const col1 = document.createElement(`div`);
- col1.className = `col-xs-7`;
- row.appendChild(col1);
- const strong = document.createElement(`strong`);
- strong.textContent = `All Rejected Rate`;
- col1.appendChild(strong);
- const col2 = document.createElement(`div`);
- col2.textContent = `${(
- (hitsOverview.approved / (hitsOverview.approved + hitsOverview.rejected + hitsOverview.pending)) *
- 100
- ).toFixed(4)}%`;
- col2.className = `col-xs-5 text-xs-right`;
- row.appendChild(col2);
- const hr = document.getElementById(`dashboard-hits-overview`).getElementsByTagName(`hr`)[1];
- hr.parentNode.insertBefore(row, hr);
- }
- function fourDigitPercents() {
- for (const row of document.getElementById(`dashboard-hits-overview`).getElementsByClassName(`row`)) {
- if (row.textContent.includes(`Approval Rate`)) {
- row.getElementsByClassName(`text-xs-right`)[0].textContent = `${(
- (hitsOverview.approved / (hitsOverview.approved + hitsOverview.rejected)) *
- 100
- ).toFixed(4)}%`;
- }
- if (row.textContent.includes(`Rejection Rate`)) {
- row.getElementsByClassName(`text-xs-right`)[0].textContent = `${(
- (hitsOverview.rejected / (hitsOverview.approved + hitsOverview.rejected)) *
- 100
- ).toFixed(4)}%`;
- }
- }
- }
- function hitStatusChanges() {
- const old = localStorage.getItem(`statusDetailsObj`) ? JSON.parse(localStorage.getItem(`statusDetailsObj`)) : {};
- localStorage.setItem(`statusDetailsObj`, JSON.stringify(statusDetailsObj));
- function applyChanges(node) {
- node.querySelectorAll(`.desktop-row`).forEach((row) => {
- const date = row.querySelector(`a`).href.split(`/status_details/`)[1];
- row.querySelectorAll(`.text-xs-right`).forEach((col) => {
- const key = col.classList[2].replace(`-column`, ``).replace(`-`, `_`);
- const change = statusDetailsObj[date][key] - (old[date] ? old[date][key] : 0);
- if (change !== 0) {
- const span = document.createElement(`span`);
- span.textContent = key.includes(`rewards`) || key.includes(`earnings`) ? `${needPlus(change)}${toMoney(change)}` : `${needPlus(change)}${change}`;
- span.style.float = `left`;
- span.style.fontSize = `70%`;
- col.appendChild(span);
- }
- });
- });
- }
- const observer = new MutationObserver((mutationsList, observer) => {
- mutationsList.forEach((mutation) => {
- const addedNode = mutation.addedNodes[0];
- if (addedNode && addedNode.classList.contains(`expanded-row`)) {
- applyChanges(addedNode);
- }
- });
- });
- observer.observe(statusDetailsTable, { childList: true, subtree: true });
- }
- function latestActivity() {
- const latest = statusDetailsArr[0];
- const date = toDate(latest.date);
- const container = document.createElement(`div`);
- container.className = `row m-b-xl`;
- const col = document.createElement(`div`);
- col.className = `col-xs-12`;
- container.appendChild(col);
- const h2 = document.createElement(`h2`);
- h2.className = `m-b-md`;
- h2.textContent = `Activity for ${date}`;
- col.appendChild(h2);
- const row = document.createElement(`div`);
- row.className = `row`;
- col.appendChild(row);
- const col2 = document.createElement(`div`);
- col2.className = `col-xs-12`;
- row.appendChild(col2);
- const border = document.createElement(`div`);
- border.className = `border-gray-lightest p-a-sm`;
- col2.appendChild(border);
- const earningsRow = document.createElement(`div`);
- earningsRow.className = `row m-b-sm`;
- border.appendChild(earningsRow);
- const earningsText = document.createElement(`div`);
- earningsText.className = `col-xs-7 col-sm-6 col-lg-7`;
- earningsRow.appendChild(earningsText);
- const earningsStrong = document.createElement(`strong`);
- earningsStrong.textContent = `Projected Earnings`;
- earningsText.appendChild(earningsStrong);
- const earningsValue = document.createElement(`div`);
- earningsValue.className = `col-xs-5 col-sm-6 col-lg-5 text-xs-right`;
- earningsValue.textContent = localStorage.todaysearnings || `$0.00`;
- earningsRow.appendChild(earningsValue);
- const bonusesRow = document.createElement(`div`);
- bonusesRow.className = `row m-b-sm`;
- border.appendChild(bonusesRow);
- const bonusesText = document.createElement(`div`);
- bonusesText.className = `col-xs-7 col-sm-6 col-lg-7`;
- bonusesRow.appendChild(bonusesText);
- const bonusesStrong = document.createElement(`strong`);
- bonusesStrong.textContent = `Bonuses`;
- bonusesText.appendChild(bonusesStrong);
- const bonusesValue = document.createElement(`div`);
- bonusesValue.className = `col-xs-5 col-sm-6 col-lg-5 text-xs-right`;
- bonusesValue.textContent = localStorage.todaysbonuses || `$0.00`;
- bonusesRow.appendChild(bonusesValue);
- const collapse = document.createElement(`div`);
- collapse.id = `TodaysActivityAdditionalInfo`;
- collapse.className = `collapse`;
- border.appendChild(collapse);
- const hr = document.createElement(`hr`);
- hr.className = `m-b-sm m-t-0`;
- collapse.appendChild(hr);
- const hr2 = document.createElement(`hr`);
- hr2.className = `m-b-sm m-t-0`;
- border.appendChild(hr2);
- const control = document.createElement(`a`);
- control.className = `collapse-more-less`;
- control.href = `#TodaysActivityAdditionalInfo`;
- control.setAttribute(`aria-controls`, `TodaysActivityAdditionalInfo`);
- control.setAttribute(`aria-expanded`, `false`);
- control.setAttribute(`data-toggle`, `collapse`);
- border.appendChild(control);
- const more = document.createElement(`span`);
- more.className = `more`;
- control.appendChild(more);
- const plus = document.createElement(`i`);
- plus.className = `fa fa-plus-circle`;
- more.appendChild(plus);
- const moreText = document.createTextNode(`\nMore\n`);
- more.appendChild(moreText);
- const less = document.createElement(`span`);
- less.className = `less`;
- control.appendChild(less);
- const minus = document.createElement(`i`);
- minus.className = `fa fa-minus-circle`;
- less.appendChild(minus);
- const lessText = document.createTextNode(`\nLess\n`);
- less.appendChild(lessText);
- const side = document.querySelector(`.col-md-push-8`);
- side.insertBefore(container, side.firstChild);
- bonusesValue.textContent = `$${latest.bonus_rewards.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
- let hitLog =
- date === localStorage.WMTD_date ? (localStorage.WMTD_hitLog ? JSON.parse(localStorage.WMTD_hitLog) : {}) : {};
- async function get(page, rescan) {
- try {
- page = Number.isInteger(page) ? page : 1;
- earningsValue.textContent = `Calculating Page ${page}`;
- const fetchURL = new URL(`https://worker.mturk.com/status_details/${date}`);
- fetchURL.searchParams.append(`page_number`, page);
- fetchURL.searchParams.append(`format`, `json`);
- const response = await fetch(fetchURL, {
- credentials: `include`,
- });
- if (response.status === 429) {
- return setTimeout(get, 2000, page, rescan);
- }
- const json = await response.json();
- for (const hit of json.results) {
- hitLog[hit.hit_id] = hit;
- }
- const logLength = Object.keys(hitLog).length;
- const expectedLength = Number(page) * 20 - 20 + json.num_results;
- if (!rescan && logLength !== expectedLength) {
- return get(1, true);
- } else {
- localStorage.WMTD_hitLog = JSON.stringify(hitLog);
- }
- localStorage.WMTD_lastPage = page;
- if (json.results.length === 20) {
- return get(++page, rescan);
- } else if (logLength !== json.total_num_results) {
- hitLog = {};
- return get(1, true);
- } else {
- let projectedEarnings = 0;
- const reqLog = {};
- for (const key in hitLog) {
- const hit = hitLog[key];
- if (hit.status !== `Rejected`) {
- projectedEarnings += hit.reward.amount_in_dollars;
- }
- if (!reqLog[hit.requester_id]) {
- reqLog[hit.requester_id] = {
- requester_id: hit.requester_id,
- requester_name: hit.requester_name,
- reward: hit.reward.amount_in_dollars,
- submitted: 1,
- };
- } else {
- reqLog[hit.requester_id].submitted += 1;
- reqLog[hit.requester_id].reward += hit.reward.amount_in_dollars;
- }
- }
- const sort = Object.keys(reqLog).sort((a, b) => reqLog[a].reward - reqLog[b].reward);
- const fragment = document.createDocumentFragment();
- for (let i = sort.length - 1; i > -1; i--) {
- const key = sort[i];
- const requester_name = reqLog[key].requester_name;
- const reward = `$${reqLog[key].reward.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
- const submitted = reqLog[key].submitted;
- const reqRow = document.createElement(`div`);
- reqRow.className = `row m-b-sm`;
- fragment.appendChild(reqRow);
- const requester = document.createElement(`div`);
- requester.className = `col-xs-6`;
- reqRow.appendChild(requester);
- const requesterStrong = document.createElement(`strong`);
- requesterStrong.textContent = requester_name;
- requester.appendChild(requesterStrong);
- const submitValue = document.createElement(`div`);
- submitValue.className = `col-xs-3 text-xs-right`;
- submitValue.textContent = submitted;
- reqRow.appendChild(submitValue);
- const rewardValue = document.createElement(`div`);
- rewardValue.className = `col-xs-3 text-xs-right`;
- rewardValue.textContent = reward;
- reqRow.appendChild(rewardValue);
- }
- collapse.appendChild(fragment);
- earningsValue.textContent = `$${projectedEarnings.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
- }
- } catch (error) {
- earningsValue.textContent = error;
- }
- }
- get(
- date === localStorage.WMTD_date ? (localStorage.WMTD_lastPage ? Number(localStorage.WMTD_lastPage) : 1) : 1,
- false,
- );
- localStorage.WMTD_date = date;
- }
- function openFirstWeek() {
- statusDetailsTable.querySelector(`.fa.expand-button.fa-plus-circle`).click();
- }
- function rejectionsBelow99() {
- const row = document.createElement(`div`);
- row.className = `row m-b-sm`;
- const col1 = document.createElement(`div`);
- col1.className = `col-xs-7`;
- row.appendChild(col1);
- const strong = document.createElement(`strong`);
- strong.textContent = `Rejections ≤ 99%`;
- col1.appendChild(strong);
- const col2 = document.createElement(`div`);
- col2.textContent = Math.round(
- (hitsOverview.rejected - 0.01 * (hitsOverview.approved + hitsOverview.rejected + hitsOverview.pending)) / -0.99,
- ).toLocaleString();
- col2.className = `col-xs-5 text-xs-right`;
- row.appendChild(col2);
- const additional = document
- .getElementById(`dashboard-hits-overview`)
- .getElementsByClassName(`border-gray-lightest`)[0];
- additional.appendChild(row);
- }
- function rejectionsBelow95() {
- const row = document.createElement(`div`);
- row.className = `row m-b-sm`;
- const col1 = document.createElement(`div`);
- col1.className = `col-xs-7`;
- row.appendChild(col1);
- const strong = document.createElement(`strong`);
- strong.textContent = `Rejections ≤ 95%`;
- col1.appendChild(strong);
- const col2 = document.createElement(`div`);
- col2.textContent = Math.round(
- (hitsOverview.rejected - 0.05 * (hitsOverview.approved + hitsOverview.rejected + hitsOverview.pending)) / -0.95,
- ).toLocaleString();
- col2.className = `col-xs-5 text-xs-right`;
- row.appendChild(col2);
- const additional = document
- .getElementById(`dashboard-hits-overview`)
- .getElementsByClassName(`border-gray-lightest`)[0];
- additional.appendChild(row);
- }
- allApprovedRate();
- allRejectedRate();
- fourDigitPercents();
- hitStatusChanges();
- latestActivity();
- openFirstWeek();
- rejectionsBelow99();
- rejectionsBelow95();