Skip to content

Instantly share code, notes, and snippets.

@cuylerstuwe
Last active July 20, 2019 21:20
Show Gist options
  • Save cuylerstuwe/0e91c22eb717f0fe88109a0cdd539b33 to your computer and use it in GitHub Desktop.
Save cuylerstuwe/0e91c22eb717f0fe88109a0cdd539b33 to your computer and use it in GitHub Desktop.
mTurk Batches - Cents-Per-Word/Decision Estimate
// ==UserScript==
// @name mTurk Batches - Cents-Per-Word/Decision Estimate (for generic, single-page, instant-load batch HITs)
// @namespace salembeats
// @version 1.2
// @description Display a quick heads-up estimate of how many cents-per-decision a given mTurk batch job offers.
// @author Cuyler Stuwe (salembeats)
// @include https://worker.mturk.com/projects/*
// @include https://www.mturkcontent.com/dynamic/hit?assignmentId=*
// @grant none
// ==/UserScript==
async function parentMain() {
window.addEventListener("message", e => {
if(e.data.askForHitValue) {
const hitValue = +document.querySelectorAll(".detail-bar-value")[3].innerText.replace("$", "");
e.source.postMessage({hitValue}, "*");
}
});
}
async function frameMain() {
const lowercaseBodyText = document.body.innerText.toLowerCase();
const isHintedAsSurvey = () => (
document.querySelector("a[href*='qualtrics']") || // Qualtrics
["survey", "completion code"].some(keyword => lowercaseBodyText.includes(keyword)) || // Keywords
document.querySelector("#surveycode") || // Stock mTurk Template
document.querySelector(".takesurveyform") // TurkPrime
);
if(isHintedAsSurvey()) { return; }
const countOfSingleChoiceElements = (
[
"input[type='checkbox']",
"input[type='text']",
"input[type='button']"
].reduce((sum, selector) => (
document.querySelectorAll(selector).length + sum
), 0)
);
const countOfRadioGroups = (
[...document.querySelectorAll("input[type='radio'][name]")]
.reduce((set, namedRadioEl) => (
set.add(namedRadioEl.getAttribute("name"))
), new Set())
.size
);
const countOfAwsButtonGroups = document.querySelectorAll(".btn-group").length;
const countOfFreeformInputs = ["input[type='text']", "textarea", "input:not([type])", "crowd-input"].reduce(
(sum, selector) => sum + document.querySelectorAll(selector).length,
0
);
const numberOfChoices = countOfSingleChoiceElements + countOfRadioGroups + countOfAwsButtonGroups + countOfFreeformInputs;
console.log("noc", numberOfChoices)
if(numberOfChoices === 0) { return; }
const hitValue = await new Promise(resolve => {
const messageListener = e => {
if(e.data.hitValue) {
window.removeEventListener("message", messageListener);
resolve(e.data.hitValue);
}
};
window.addEventListener("message", messageListener);
window.parent.postMessage({askForHitValue: true}, "*");
});
const centsPerDecision = ((hitValue / numberOfChoices) * 100).toFixed(1);
const wordCountEstimate = document.body.innerText.split(" ").length;
const centsPerHundredWords = ((hitValue / (wordCountEstimate / 100)) * 100).toFixed(1);
document.body.insertAdjacentHTML("afterbegin", `
<style>
.estimate_container {
border: 1px dotted black;
padding: 2px;
margin: 2px;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.action_type {
color: rgb(96, 96, 96);
}
.cents_per_decision_line {
}
.cents_per_word_line {
}
.major_value {
padding: 2px;
font-weight: bold;
border-left: 1px dotted rgb(64, 64, 64);
border-right: 1px dotted rgb(64, 64, 64);
}
.minor_value {
padding: 1px;
border-bottom: 1px dotted rgb(64, 64, 64);
}
.normal_breakdown_line {
}
.freeform_input_warning_line {
background: rgba(255, 255, 0, 0.3);
}
</style>
<div class="estimate_container">
<div class="cents_per_word_line">
<strong>CPW</strong> (<span class="action_type">Reading</span>):
<span class="major_value">${centsPerHundredWords}</span> Cents Per 100 Words.
</div>
<div class="cents_per_decision_line">
<strong>CPD</strong> (<span class="action_type">Doing</span>):
<span class="major_value">${centsPerDecision}</span> Cents Per Decision.
</div>
<div class="normal_breakdown_line">
<span class="minor_value">${countOfSingleChoiceElements}</span> x Single-Choice,
<span class="minor_value">${countOfRadioGroups}</span> x Radio Group,
<span class="minor_value">${countOfAwsButtonGroups}</span> x AWS Button Group.
</div>
<div class="freeform_input_warning_line">
⚠ <span class="minor_value">${countOfFreeformInputs}</span> x Text Input.
</div>
</div>
`);
[...document.querySelectorAll(".estimate_container")].slice(1).forEach(extraEstimateContainer => extraEstimateContainer.remove());
}
async function main() {
if(window.location.href.includes("worker.mturk.com")) {
parentMain();
}
else {
frameMain();
new MutationObserver(mutations => {
if(!document.querySelector(".estimate_container")) {
frameMain();
}
}).observe(document.body, {childList: true, subtree: true, attributes: true});
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment