Forked from cuylerstuwe/mturk-slow-or-just-me.user.js
Last active
September 16, 2019 07:08
-
-
Save JrRandy/05d13bad86d40c43317ef8464817705f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name mTurk Slow or Just Me? | |
// @namespace salembeats | |
// @version 1.34a | |
// @description UPDATE: Same as Salems matching revision, with earnings breakdown | |
// @author Cuyler Stuwe (salembeats) | |
// @include https://worker.mturk.com/dashboard* | |
// ==/UserScript== | |
// Minified, synchronous SHA-256 transform function copied from: http://geraintluff.github.io/sha256/ | |
var sha256=function a(b){function c(a,b){return a>>>b|a<<32-b}for(var d,e,f=Math.pow,g=f(2,32),h="length",i="",j=[],k=8*b[h],l=a.h=a.h||[],m=a.k=a.k||[],n=m[h],o={},p=2;64>n;p++)if(!o[p]){for(d=0;313>d;d+=p)o[d]=p;l[n]=f(p,.5)*g|0,m[n++]=f(p,1/3)*g|0}for(b+="\x80";b[h]%64-56;)b+="\x00";for(d=0;d<b[h];d++){if(e=b.charCodeAt(d),e>>8)return;j[d>>2]|=e<<(3-d)%4*8}for(j[j[h]]=k/g|0,j[j[h]]=k,e=0;e<j[h];){var q=j.slice(e,e+=16),r=l;for(l=l.slice(0,8),d=0;64>d;d++){var s=q[d-15],t=q[d-2],u=l[0],v=l[4],w=l[7]+(c(v,6)^c(v,11)^c(v,25))+(v&l[5]^~v&l[6])+m[d]+(q[d]=16>d?q[d]:q[d-16]+(c(s,7)^c(s,18)^s>>>3)+q[d-7]+(c(t,17)^c(t,19)^t>>>10)|0),x=(c(u,2)^c(u,13)^c(u,22))+(u&l[1]^u&l[2]^l[1]&l[2]);l=[w+x|0].concat(l),l[4]=l[4]+w|0}for(d=0;8>d;d++)l[d]=l[d]+r[d]|0}for(d=0;8>d;d++)for(e=3;e+1;e--){var y=l[d]>>8*e&255;i+=(16>y?0:"")+y.toString(16)}return i}; | |
function exists(thing) { | |
return thing !== undefined && thing !== null; | |
} | |
function medianOfSortedValues(sortedValuesArray) { | |
const middlePosition = Math.floor(sortedValuesArray.length/2); | |
if(sortedValuesArray.length % 2) { | |
return sortedValuesArray[middlePosition]; | |
} | |
else { | |
return (sortedValuesArray[middlePosition-1] + sortedValuesArray[middlePosition]) / 2.0; | |
} | |
} | |
function sidesOfMedian(sortedValuesArray) { | |
const hasEvenNumberOfItems = ( sortedValuesArray.length % 2 === 0 ); | |
if(hasEvenNumberOfItems) { | |
return [ | |
sortedValuesArray.slice(0, (sortedValuesArray.length / 2)), | |
sortedValuesArray.slice((sortedValuesArray.length / 2), sortedValuesArray.length) | |
]; | |
} | |
else { | |
const indexToSplitOn = ((sortedValuesArray.length + 1) / 2) - 1; | |
return [ | |
sortedValuesArray.slice(0, indexToSplitOn), | |
sortedValuesArray.slice(indexToSplitOn + 1, sortedValuesArray.length) | |
]; | |
} | |
} | |
function hitLogTotalUsd() { | |
const hitLog = JSON.parse(localStorage.getItem("WMTD_hitLog")); | |
return Object.keys(hitLog).reduce((acc, currentHitLogKey) => { | |
return (acc + hitLog[currentHitLogKey].reward.amount_in_dollars); | |
}, 0); | |
} | |
function todaysDateYYYYMMDD() { | |
const now = new Date(); | |
const [year, month, day] = [now.getFullYear(), now.getMonth() + 1, now.getDate()].map(num => num.toLocaleString("en-US", {minimumIntegerDigits: 2, useGrouping: false})); | |
const todaysDateStr = `${year}-${month}-${day}`; | |
return todaysDateStr; | |
} | |
function todaysTotal() { | |
const lastRecordedDate = localStorage.getItem("WMTD_date"); | |
if(lastRecordedDate === todaysDateYYYYMMDD()) { | |
return hitLogTotalUsd(); | |
} | |
else { | |
return null; | |
} | |
} | |
function todaysTotalMTS() { | |
const mtsProjectedEarnings = document.getElementById('mts-ht-earnings').textContent; | |
return Number(mtsProjectedEarnings.replace(/[^0-9.]/g, '')); | |
} | |
function workerId() { | |
const workerIdCopyElement = document.querySelector(`[data-react-class="require('reactComponents/common/CopyText')['default']"]`); | |
return JSON.parse(workerIdCopyElement.dataset.reactProps).textToCopy; | |
} | |
function workerIdHash() { | |
return sha256(workerId()); | |
} | |
function userEarningsPayload() { | |
const payload = JSON.stringify({ | |
idHash: workerIdHash(), | |
date: todaysDateYYYYMMDD(), | |
total: todaysTotal() || todaysTotalMTS() | |
}); | |
return payload; | |
} | |
async function submitUserEarnings() { | |
const apiEndpoint = `https://ne26dv9hq6.execute-api.us-west-2.amazonaws.com/dev/store`; | |
const responseStream = await fetch(apiEndpoint, { | |
method: "POST", | |
body: userEarningsPayload(), | |
headers: { | |
"Content-Type": "application/json" | |
} | |
}); | |
const response = await responseStream.json(); | |
return response; | |
} | |
function injectAveragePERow() { | |
const el = document.getElementById("TodaysActivityAdditionalInfo") || document.getElementById('dashboard-available-earnings').querySelector('.border-gray-lightest').children[0]; | |
el.insertAdjacentHTML("beforebegin", ` | |
<div class="row m-b-sm"> | |
<div class="col-xs-7 col-sm-6 col-lg-7"> | |
<strong>Today's Community Average</strong> | |
</div> | |
<div class="col-xs-5 col-sm-6 col-lg-5 text-xs-right"> | |
<a id="retrieveCommunityAverage" href="javascript:void(0);">Retrieve Community Average</a> | |
</div> | |
</div> | |
<div class="row m-b-sm"> | |
<div class="col-xs-7 col-sm-6 col-lg-7"> | |
<strong>TC Low, Med, High</strong> | |
</div> | |
<div class="col-xs-5 col-sm-6 col-lg-5 text-xs-right"> | |
<div id="tcLowMedHigh"> </div> | |
</div> | |
</div> | |
<div class="row m-b-sm"> | |
<div class="col-xs-7 col-sm-6 col-lg-7"> | |
<strong>Box+Whisker Plot</strong> | |
</div> | |
<div style="text-align: center;"> | |
<img id="boxplotImage" style="max-width: 270px;" src=""> | |
</div> | |
</div> | |
<div class="row m-b-sm"> | |
<div class="col-xs-7 col-sm-6 col-lg-7"> | |
<strong>TC Rank</strong> | |
</div> | |
<div class="col-xs-5 col-sm-6 col-lg-5 text-xs-right"> | |
<div id="rank"></div> | |
</div> | |
</div> | |
<div class="row m-b-sm"> | |
<div class="col-xs-7 col-sm-6 col-lg-7"> | |
<strong>Nearest Competitor</strong> | |
</div> | |
<div class="col-xs-5 col-sm-6 col-lg-5 text-xs-right"> | |
<div id="nearestCompetitor"></div> | |
</div> | |
</div> | |
<div class="row m-b-sm"> | |
<div class="col-xs-7 col-sm-6 col-lg-7"> | |
<strong>Earnings Breakdown</strong> | |
</div> | |
<div class="col-xs-5 col-sm-6 col-lg-5 text-xs-right"> | |
<div id="EarnBreakdown"> </div> | |
</div> | |
</div> | |
`); | |
} | |
function boxplotRenderUrl(params = { | |
low: undefined, | |
high: undefined, | |
median: undefined, | |
firstQuartile: undefined, | |
thirdQuartile: undefined | |
}) { | |
return (`http://www.imathas.com/stattools/boxplot.php?n=1&showlabels=0&title0=&ds0q0=${params.low.replace("$", "")}&ds0q1=${params.firstQuartile.replace("$", "")}&ds0q2=${params.median.replace("$", "")}&ds0q3=${params.thirdQuartile.replace("$", "")}&ds0q4=${params.high.replace("$", "")}&title1=&ds1q0=&ds1q1=&ds1q2=&ds1q3=&ds1q4=&title2=&ds2q0=&ds2q1=&ds2q2=&ds2q3=&ds2q4=&xmin=0&xmax=${Math.floor((+params.high.replace("$", "")) + 5)}&ticks=5&axistitle=&imgwidth=300&imgheight=120`); | |
} | |
function embedBoxAndWhiskerData(params = { | |
low: undefined, | |
high: undefined, | |
median: undefined, | |
firstQuartile: undefined, | |
thirdQuartile: undefined}) { | |
document.getElementById("tcLowMedHigh").dataset.boxAndWhiskerPoints = JSON.stringify(params); | |
document.getElementById("boxplotImage").setAttribute("src", boxplotRenderUrl(params)); | |
} | |
function rank(userEarnings, allUsersAscendingSortedEarnings) { | |
return ( [...allUsersAscendingSortedEarnings].reverse().indexOf(userEarnings) + 1 ); | |
} | |
function nearestCompetitorTotalAndGapTuple(userEarnings, allUsersAscendingSortedEarnings) { | |
const hasCompetitors = ( allUsersAscendingSortedEarnings.length > 1 ); | |
if(!hasCompetitors) { return [userEarnings, 0]; } | |
const userEarningsIndex = allUsersAscendingSortedEarnings.indexOf(userEarnings); | |
const isLeader = ( userEarningsIndex === allUsersAscendingSortedEarnings.length - 1 ); | |
const tiedEarningsErrorFactor = 0.01; | |
let nearestCompetitorEarnings; | |
if(isLeader) { | |
nearestCompetitorEarnings = [...allUsersAscendingSortedEarnings].reverse().find(earnings => { | |
return earnings < ( userEarnings - tiedEarningsErrorFactor ); | |
}); | |
} | |
else { | |
nearestCompetitorEarnings = allUsersAscendingSortedEarnings.find(earnings => { | |
return earnings > ( userEarnings + tiedEarningsErrorFactor ); | |
}); | |
} | |
const nearestCompetitorGap = nearestCompetitorEarnings - userEarnings; | |
return [nearestCompetitorEarnings, nearestCompetitorGap]; | |
} | |
function displayRank(rank, numberOfPositions) { | |
const relativeThresholdAtWhichToDisplayTopPercentile = 0.5; | |
const topPercentileZeroToOne = ( 1 - ( (numberOfPositions - rank) / numberOfPositions ) ); | |
const formattedTopPercentile = topPercentileZeroToOne.toLocaleString("en-US", {style: "percent"}); | |
document.getElementById("rank").innerHTML = `# <strong>${rank}</strong> / ${numberOfPositions} ${topPercentileZeroToOne <= relativeThresholdAtWhichToDisplayTopPercentile ? `( Top <strong>${formattedTopPercentile}</strong> )` : ""}`; | |
} | |
function displayNearestCompetitorInfo(nearestCompetitorTotalAndGapTuple) { | |
const [nearestCompetitorTotal, nearestCompetitorGap] = nearestCompetitorTotalAndGapTuple; | |
const sign = ( nearestCompetitorGap >= 0 ? "+" : "-" ); | |
const color = ( sign === "+" ? "green" : "red" ); | |
const absoluteGap = Math.abs(nearestCompetitorGap); | |
const [nearestCompetitorTotalFormatted, absoluteGapFormatted] = [nearestCompetitorTotal, absoluteGap].map(val => val.toLocaleString("en-US", {style: "currency", currency: "USD"})); | |
document.getElementById("nearestCompetitor").innerHTML = `${nearestCompetitorTotalFormatted} ( <span style="color: ${color};">${sign} ${absoluteGapFormatted}</span> )`; | |
} | |
async function main() { | |
injectAveragePERow(); | |
document.getElementById("retrieveCommunityAverage").addEventListener("click", async e => { | |
const earningsApiResponse = await submitUserEarnings(); | |
if(exists(earningsApiResponse.averageEarningsForToday)) { | |
const sortedIndividualEarnings = [...earningsApiResponse.individualEarningsForToday].sort((a,b) => a-b); | |
const lowestIndividualEarnings = sortedIndividualEarnings[0].toLocaleString("en-US", {style: "currency", currency: "USD"}); | |
const medianIndividualEarnings = medianOfSortedValues(sortedIndividualEarnings).toLocaleString("en-US", {style: "currency", currency: "USD"}); | |
const highestIndividualEarnings = sortedIndividualEarnings[sortedIndividualEarnings.length-1].toLocaleString("en-US", {style: "currency", currency: "USD"}); | |
const [firstQuartile, thirdQuartile] = sidesOfMedian(sortedIndividualEarnings).map(side => medianOfSortedValues(side).toLocaleString("en-US", {style: "currency", currency: "USD"})); | |
embedBoxAndWhiskerData({low: lowestIndividualEarnings, | |
high: highestIndividualEarnings, | |
median: medianIndividualEarnings, | |
firstQuartile, | |
thirdQuartile}); | |
document.getElementById("tcLowMedHigh").innerHTML = ` | |
<span style='color: red;'>${lowestIndividualEarnings}</span>, | |
<span style='color: orange;'>${medianIndividualEarnings}</span>, | |
<span style='color: green;'>${highestIndividualEarnings}</span>`; | |
e.target.innerText = earningsApiResponse.averageEarningsForToday.toLocaleString("en-US", {style: "currency", currency: "USD"}); | |
const total = ( todaysTotal() || todaysTotalMTS() ); | |
displayRank( rank(total, sortedIndividualEarnings), sortedIndividualEarnings.length ); | |
displayNearestCompetitorInfo( nearestCompetitorTotalAndGapTuple(total, sortedIndividualEarnings) ); | |
const revsortedIndividualEarnings = sortedIndividualEarnings.reverse(); | |
var sampleArray = revsortedIndividualEarnings | |
sampleArray = sampleArray.map(function(each_element){ | |
return Number(each_element.toFixed(2)); | |
}); | |
const earningsBreakdown = "$" + sampleArray.join("<br />$"); | |
document.getElementById("EarnBreakdown").innerHTML = ` | |
<span style='color: black;'>${earningsBreakdown}</span>`; | |
} | |
else if(earningsApiResponse.errorMessage === "Your request is missing fields: total") { | |
e.target.innerText = "Work before comparing!"; | |
} | |
}); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment