turkopticon-async

Review requesters on Amazon Mechanical Turk

  1. // ==UserScript==
  2. // @name turkopticon-async
  3. // @version 2016.05.12.1002
  4. // @description Review requesters on Amazon Mechanical Turk
  5. // @author feihtality
  6. // @include https://*.mturk.com/*
  7. // @exclude https://*.mturk.com/mturk/findhits?*hit_scraper
  8. // @namespace https://greasyfork.org/users/12709
  9. // ==/UserScript==
  10. /*jshint esnext:true*/
  11.  
  12. /*
  13. *
  14. * fork of 'turkopticon' by Lilly Irani and Six Silberman --
  15. * * turkopticon-async requests data from the Turkopticon servers asynchronously which improves
  16. * performance and prevents the page from locking up until the request completes
  17. * * fixes flickering and other minor issues
  18. * * HIT Scraper-consistent coloring
  19. *
  20. */
  21.  
  22. // TODO: clean-up
  23.  
  24.  
  25. (function() {
  26. 'use strict';
  27.  
  28. var TURKOPTICON_BASE = "https://turkopticon.ucsd.edu/";
  29. var API_BASE = "https://turkopticon.ucsd.edu/api/";
  30. var API_MULTI_ATTRS_URL = API_BASE + "multi-attrs.php?ids=";
  31.  
  32. function makeXhrQuery(rai) {
  33. return new Promise( function(accept, reject) {
  34. var xhr = new XMLHttpRequest(), url = API_MULTI_ATTRS_URL + Object.keys(rai).join(',');
  35. xhr.open('GET', url, true);
  36. xhr.responseType = 'json';
  37. xhr.send();
  38. xhr.onload = e => accept(e.target.response);
  39. xhr.onerror = xhr.ontimeout = e => reject(e.target);
  40. }); }
  41.  
  42. function ul(cl, inner) {
  43. return "<ul class='" + cl + "'>" + inner + "</ul>"; }
  44.  
  45. function li(cl, inner) {
  46. return "<li class='" + cl + "'>" + inner + "</li>"; }
  47.  
  48. function span(cl, inner) {
  49. return "<span class='" + cl + "'>" + inner + "</span>"; }
  50.  
  51. function pad(word, space) {
  52. if (word.length >= space) { return word; }
  53. else { return word + '&nbsp;'.repeat(space - word.length); } }
  54.  
  55. function long_word(word) {
  56. switch(word) {
  57. case "comm": return "communicativity";
  58. case "pay" : return "generosity";
  59. case "fair": return "fairness";
  60. case "fast": return "promptness"; } }
  61.  
  62. function attr_html(n, i) {
  63. var bar = `<meter min="0.8" low="2.5" high="3.4" optimum="5" max="5" value=${i} style="width:120px"></meter>`;
  64. return pad(long_word(n), 15) + ": " + bar + "&nbsp;" + i + " / 5"; }
  65.  
  66. function ro_html(attrs) {
  67. var rohtml = [];
  68. for (var attr of Object.keys(attrs))
  69. rohtml.push( li("attr", attr_html(attr, attrs[attr])) );
  70. rohtml.push(li("gray_link", "<a href='" + TURKOPTICON_BASE + "help#attr'>What do these scores mean?</a>"));
  71. return rohtml.join(''); }
  72.  
  73. function nrs(rid, nrevs) {
  74. var str = "";
  75. if (typeof nrevs === 'undefined') {
  76. str = "<li>No reviews for this requester</li>"; }
  77. else { str = "<li>Scores based on <a href='" + TURKOPTICON_BASE + rid + "'>" + nrevs + " reviews</a></li>"; }
  78. return str; }
  79.  
  80. function tos(tosflags) {
  81. return tosflags ? "<li>Terms of Service violation flags: " + tosflags + "</li>" : ''; }
  82.  
  83. function rl(rid, name) {
  84. return "<li><a href='" + TURKOPTICON_BASE + "report?requester[amzn_id]=" + rid +
  85. "&requester[amzn_name]=" + name + "'>Report your experience with this requester &raquo;</a></li>"; }
  86.  
  87. function insertInlineCss() {
  88. document.head.appendChild(document.createElement('STYLE')).innerHTML =
  89. ".tob, .tom { list-style-type: none; padding-left: 0; }\n"
  90. + ".tob { float: left; cursor:default; margin-right:5px; }\n"
  91. + ".tob > .tom { display: none; position: absolute; background-color: #ebe5ff; border: 1px solid #aaa; padding: 5px; margin:0; z-index:10; }\n"
  92. + ".tob:hover > .tom { display: block; }\n"
  93. + ".tob > li { border: 1px solid #9db9d1; background-color: #ebe5ff; color: #00c; padding: 3px 3px 1px 3px; }\n"
  94. + ".tob > li.toc { color: #f33; }\n"
  95. + ".tom > li { line-height:14px; font-size:12px; }\n"
  96. //+ "@media screen and (-webkit-min-device-pixel-ratio:0) { \n .tob { margin-top: -5px; } \n}\n"
  97. + ".attr { font-family: Monaco, Courier, monospace; color: #333; }\n"
  98. + ".bar { font-size: 0.6em; }\n"
  99. + ".gray_link { margin-bottom: 15px; }\n"
  100. + ".gray_link a { color: #666; }";}
  101.  
  102. function insertDropDowns(rai, resp) {
  103. for(var rid in rai) {
  104. if (rai.hasOwnProperty(rid)) {
  105. for(var i = 0; i < rai[rid].length; i++) {
  106. var td = rai[rid][i].node;
  107. td.innerHTML = dropDown(resp[rid], rid) + ' ' + td.innerHTML; } } } }
  108.  
  109. function getAnchors() {
  110. var a = {}, id, name,
  111. add = (id,obj) => id in a ? a[id].push(obj) : a[id] = [ obj ],
  112. fns = [ function() {
  113. [].forEach.call(document.querySelectorAll('.requesterIdentity'), v => {
  114. id = v.parentNode.href.match(/Id=([^&]+)/)[1]; name = v.textContent;
  115. add(id, { name:name, node:v.parentNode });
  116. });
  117. if (!Object.keys(a).length) throw 0;
  118. }, function() {
  119. var node = document.querySelector('a[id|="requester.tooltip"]').parentNode.nextElementSibling;
  120. id = document.querySelector('input[name=requesterId]').value;
  121. name = document.querySelector('input[name=prevRequester]').value;
  122. add(id, { name:name, node:node });
  123. }, function() {
  124. [].forEach.call(document.querySelectorAll('span.requester-column > a[href^=h]'), v => {
  125. id = v.href.match(/Id=([^&]+)/)[1]; name = v.textContent;
  126. add(id, { name:name, node:v });
  127. });
  128. } ];
  129. for (var fn of fns) try { fn(); break; } catch(e) {}
  130. return Object.keys(a).length && a;
  131. }
  132. var TO = function(id, nodeObj, dataObj) {
  133. dataObj = dataObj || {};
  134. var avg = 'attrs' in dataObj ? _getSimpleWeightedAverage(dataObj.attrs) : 0;
  135. function _getSimpleWeightedAverage(attrs){
  136. var n = 0, d = 0, weights = { comm:1, pay:3, fair:3, fast:1 };
  137. for (var attr of Object.keys(attrs)) {
  138. n += attrs[attr] * weights[attr];
  139. d += weights[attr];
  140. }
  141. return (n/d).toFixed(3);
  142. }
  143. function _getColor(num) { return (!num || dataObj.reviews < 5) ? '#657b83' : num > 4 ? '#6ffa3c' : num > 3 ? '#d9fc35' : num > 2 ? '#fbde2d' : '#fa6f50'; }
  144. function _makeSvg() {
  145. var svg = mdom.makeNS('svg', { height:20, width:20 }),
  146. path = mdom.makeNS('path', { fill:_getColor(avg), d:'M10 0c-5.52 0-10 4.48-10 10 0 5.52 4.48 10 10 10 5.52 0 10-4.48 10-10 0-5.52-4.48-10-10-10zm4.22 5.38c1.34 0 2.41 0.42 3.22 1.25 0.81 0.83 1.22 2.02 1.22 3.5 0 1.47-0.39 2.61-1.19 3.44-0.8 0.83-1.88 1.25-3.22 1.25-1.36 0-2.45-0.42-3.25-1.25-0.8-0.83-1.19-1.95-1.19-3.41 0-0.93 0.13-1.71 0.41-2.34 0.21-0.46 0.49-0.88 0.84-1.25 0.36-0.37 0.76-0.63 1.19-0.81 0.57-0.24 1.23-0.37 1.97-0.37zm-12.47 0.16h7.25v1.56h-2.72v7.56h-1.84v-7.56h-2.69v-1.56zm12.5 1.44c-0.76 0-1.38 0.26-1.84 0.78-0.46 0.52-0.69 1.29-0.69 2.34 0 1.03 0.21 1.81 0.69 2.34 0.48 0.53 1.11 0.81 1.84 0.81 0.73 0 1.31-0.28 1.78-0.81 0.47-0.53 0.72-1.32 0.72-2.37 0-1.05-0.23-1.83-0.69-2.34-0.46-0.51-1.05-0.75-1.81-0.75z' });
  147. svg.appendChild(path);
  148. return svg;
  149. }
  150. function _makeInfoPanel() {
  151. var main = mdom.make('ul', { class: 'tom' }), html = [];
  152. if ('attrs' in dataObj) html.push(ro_html(dataObj.attrs));
  153. html.push(nrs(id, dataObj.reviews));
  154. html.push(tos(dataObj.tos_flags));
  155. html.push(rl(id,nodeObj[0].name));
  156. main.innerHTML = html.join('');
  157. return main;
  158. }
  159. return { insertBefore: node => {
  160. var _node = node[0] ? node[0].node : node;
  161. var s = mdom.make('span', { class: 'tob', style:_node.parentNode.tagName === 'TD' ? 'margin-top:-3px' : '' });
  162. s.appendChild(_makeSvg());
  163. s.appendChild(_makeInfoPanel());
  164. if (node instanceof Array) node.forEach((v,i) => i > 0 ? v.node.parentNode.insertBefore(s.cloneNode(true), v.node) : v.node.parentNode.insertBefore(s,v.node));
  165. else node.parentNode.insertBefore(s, node);
  166. } };
  167. },
  168. mdom = (function(){
  169. function make(el,attrs) { return setAttributes(document.createElement(el),attrs); }
  170. function makeNS(el,attrs) { return setAttributes(document.createElementNS('http://www.w3.org/2000/svg',el),attrs); }
  171. function setAttributes(el,attrs) {
  172. for (var a of Object.keys(attrs)) el.setAttribute(a, attrs[a]);
  173. return el;
  174. }
  175. return { make:make, makeNS:makeNS, setAttributes:setAttributes };
  176. })();
  177.  
  178. console.log('toa hook');
  179. insertInlineCss();
  180. var anchors = getAnchors();
  181. if (!anchors) return;
  182.  
  183. makeXhrQuery(anchors).then(function(r) {
  184. for (var id of Object.keys(r)) TO(id, anchors[id], r[id]).insertBefore(anchors[id]);
  185. }, err => console.warn(err.statusText, err));
  186.  
  187. })();