[MTurk Worker] Dashboard Enhancer

Brings many enhancements to the MTurk Worker Dashboard.

目前为 2019-02-19 提交的版本。查看 最新版本

// ==UserScript==
// @name         [MTurk Worker] Dashboard Enhancer
// @namespace    http://kadauchi.com/
// @version      2.0.5
// @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==

const dashboard = new Object();

(function () {
  Object.assign(String.prototype, {
    toNumberDE () {
      return Number(this.replace(/[^0-9.]/g, ``));
    }
  });

  Object.assign(Number.prototype, {
    toNumberDE () {
      return this;
    }
  });

  const rows = document.getElementById(`dashboard-hits-overview`).getElementsByClassName(`row`);

  dashboard.hits_overview = {
    approved: rows[0].getElementsByClassName(`text-xs-right`)[0].textContent.toNumberDE(),
    pending: rows[2].getElementsByClassName(`text-xs-right`)[0].textContent.toNumberDE(),
    rejected: rows[3].getElementsByClassName(`text-xs-right`)[0].textContent.toNumberDE(),
  };
  dashboard.daily_hit_statistics_overview = {
    total: {
      submitted: 0,
      approved: 0,
      rejected: 0,
      pending: 0,
      earnings: 0,
    }
  };
  dashboard.earnings_to_date = {
    bonuses: document.getElementById(`dashboard-earnings-to-date`).getElementsByClassName(`text-xs-right`)[2].textContent.toNumberDE()
  };

  for (const row of document.getElementsByClassName(`daily_hit_statuses`)) {
    const col = row.children;
    const date = col[0].children[0].href.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/)[0];

    dashboard.daily_hit_statistics_overview[date] = new Object();

    [`submitted`, `approved`, `rejected`, `pending`, `earnings`].forEach((currentValue, index) => {
      const value = col[index + 1].textContent.toNumberDE();

      dashboard.daily_hit_statistics_overview[date][currentValue] = value;
      dashboard.daily_hit_statistics_overview.total[currentValue] += value;
    });
  }
})();

(function allApprovedRate() {
  const overview = dashboard.hits_overview;

  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 = `${((overview.approved + overview.pending) / (overview.approved + overview.pending + overview.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 overview = dashboard.hits_overview;

  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 = `${(overview.approved / (overview.approved + overview.rejected + overview.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() {
  const overview = dashboard.hits_overview;

  for (const row of document.getElementById(`dashboard-hits-overview`).getElementsByClassName(`row`)) {
    if (row.textContent.indexOf(`Approval Rate`) !== -1) {
      row.getElementsByClassName(`text-xs-right`)[0].textContent = `${(overview.approved / (overview.approved + overview.rejected) * 100).toFixed(4)}%`;
    }
    if (row.textContent.indexOf(`Rejection Rate`) !== -1) {
      row.getElementsByClassName(`text-xs-right`)[0].textContent = `${(overview.rejected / (overview.approved + overview.rejected) * 100).toFixed(4)}%`;
    }
  }
})();

(function rejectionsBelow99() {
  const overview = dashboard.hits_overview;

  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((overview.rejected - (0.01 * (overview.approved + overview.rejected + overview.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 overview = dashboard.hits_overview;

  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((overview.rejected - (0.05 * (overview.approved + overview.rejected + overview.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);
})();

(function totalLast45Days() {
  const table = document.querySelector(`.mturk-table.hits-statuses`);

  const row = table.insertRow();
  row.className = `daily_hit_statuses`;

  const date = row.insertCell(0);
  date.textContent = `Total`;
  date.className = `hidden-xs-down col-sm-2 col-md-2`;

  const submitted = row.insertCell(1);
  submitted.textContent = dashboard.daily_hit_statistics_overview.total.submitted;
  submitted.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

  const approved = row.insertCell(2);
  approved.textContent = dashboard.daily_hit_statistics_overview.total.approved;
  approved.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

  const rejected = row.insertCell(3);
  rejected.textContent = dashboard.daily_hit_statistics_overview.total.rejected;
  rejected.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

  const pending = row.insertCell(4);
  pending.textContent = dashboard.daily_hit_statistics_overview.total.pending;
  pending.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;

  const earnings = row.insertCell(5);
  earnings.textContent = `$${dashboard.daily_hit_statistics_overview.total.earnings.toLocaleString(`en-US`, { minimumFractionDigits: 2 })}`;
  earnings.className = `text-xs-right col-xs-3 col-sm-2 col-md-2`;
})();

(function hitStatusChanges() {
  const oldDashboard = localStorage.dashboard ? JSON.parse(localStorage.dashboard) : null;
  localStorage.dashboard = JSON.stringify(dashboard);

  for (const row of document.getElementsByClassName(`daily_hit_statuses`)) {
    const col = row.children;
    const date = col[0].children[0] ? col[0].children[0].href.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/)[0] : `total`;

    [`submitted`, `approved`, `rejected`, `pending`, `earnings`].forEach((currentValue, index) => {
      const value = col[index + 1].textContent.toNumberDE();
      const oldValue = oldDashboard ? oldDashboard.daily_hit_statistics_overview[date][currentValue] : 0;

      if (value !== oldValue) {
        const change = value - oldValue;
        const changeString = change.toFixed(2).toString().replace(`.00`, ``);

        if (Math.round(change * 100) !== 0) {
          const span = document.createElement(`span`);
          span.textContent = change > 0 ? `+${changeString}` : changeString;
          span.style.float = `left`;
          span.style.fontSize = `70%`;
          col[index + 1].appendChild(span);
        }
      }
    });
  }
})();

(function todaysActivity() {
  const today = document.querySelector(`a[href^="/status_details/"]`);
  const date = today.href.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/)[0];

  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 = `${today.textContent}'s Activity`;
  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);

  if (date === localStorage.WMTD_date) {
    if (!localStorage.WMTD_bonusStart) {
      localStorage.WMTD_bonusStart = dashboard.earnings_to_date.bonuses;
    }
  }
  else {
    if (dashboard.daily_hit_statistics_overview[date].approved === 0) {
      localStorage.WMTD_bonusStart = dashboard.earnings_to_date.bonuses - dashboard.daily_hit_statistics_overview[date].earnings;
    }
    else {
      localStorage.WMTD_bonusStart = dashboard.earnings_to_date.bonuses;
    }
  }

  bonusesValue.textContent = `$${(dashboard.earnings_to_date.bonuses - localStorage.WMTD_bonusStart).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, url, 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 = page.toNumberDE() * 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 = new Object();
        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 ? localStorage.WMTD_lastPage.toNumberDE() : 1 : 1, false);
  localStorage.WMTD_date = date;

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址