mmmturkeybacon Color Coded Search with Checkpoints

Changes the title row of a HIT's description to match the average of

当前为 2014-07-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name mmmturkeybacon Color Coded Search with Checkpoints
  3. // @author mmmturkeybacon
  4. // @description Changes the title row of a HIT's description to match the average of
  5. // it's Turkopticon ratings. Changes the color of the reward amount to
  6. // match the color of the Turkopticon rating for pay. Adds colored
  7. // checkboxes to show/hide HITs by color rating. Adds a gray checkbox
  8. // to show only HITs for which you are not qualified. Changes the
  9. // background color of the HIT title and link to white for Master's HITs.
  10. // Changes the color of HITs for which you are not qualified to a darker
  11. // gray. Changes the color of visited links to black. Automatically clicks
  12. // "Show all details".
  13. // Adds checkboxes next to HIT links so that you can set a checkpoint.
  14. // A checkpoint will notify you that you've already seen a HIT by
  15. // changing the HIT link to display the date the checkpoint was set. A
  16. // well-placed checkpoint is useful when browsing HITs by creation date
  17. // (newest first) because it will alert you that you've already seen the
  18. // checkpoint HIT and probably all the HITs that come after it.
  19. // It's best to place a checkpoint on a HIT that won't be recreated
  20. // because recreated HITs jump to the first page.
  21. // This script is not a substitute for actually reading Turkopticon reviews.
  22. // @namespace http://userscripts.org/users/523367
  23. // @match https://*.mturk.com/mturk/viewhits*
  24. // @match https://*.mturk.com/mturk/findhits*
  25. // @match https://*.mturk.com/mturk/sorthits*
  26. // @match https://*.mturk.com/mturk/searchbar*
  27. // @match https://*.mturk.com/mturk/viewsearchbar*
  28. // @match https://*.mturk.com/mturk/sortsearchbar*
  29. // @match https://*.mturk.com/mturk/preview?*
  30. // @match https://*.mturk.com/mturk/return*
  31. // @require http://code.jquery.com/jquery-latest.min.js
  32. // @version 3.03
  33. // @grant GM_xmlhttpRequest
  34. // @grant GM_getValue
  35. // @grant GM_setValue
  36. // @grant GM_deleteValue
  37. // ==/UserScript==
  38.  
  39. /**********************************************************************/
  40. /* NB: turkopticon.ucsd.edu (TO website) uses yellow for a rating between 2 and 3,
  41. * but TO extension uses orange
  42. *
  43. * Turkopticon scale
  44. * green : 3 < average <= 5
  45. * orange: 2 < average <= 3
  46. * red : 1 < average <= 2
  47. *
  48. * Color Coded Search scale (so that green represents the very best HITs)
  49. * green : 4 < average <= 5
  50. * yellow: 3 < average <= 4 yellow are OK HITs, that's why the yellow has a touch of green
  51. * orange: 2 < average <= 3
  52. * red : 1 < average <= 2
  53. */
  54. var GREEN = '#66CC66'; // (4,5]
  55. //var YELLOW = '#FFFF00'; // (3,4] yellow
  56. //var YELLOW = '#CDFF2F'; // (3,4] yellow with hint of green
  57. var YELLOW = '#ADFF2F'; // (3,4] green yellow
  58. //var YELLOW = '#9CE62A'; // (3,4] darker green yello
  59. var ORANGE = '#FF9900'; // (2,3]
  60. //var RED = '#FF0000'; // (1,2]
  61. var RED = '#FF3030'; // (1,2]
  62. var BLUE = '#7FAAEB'; // no rating
  63.  
  64. var VISITED_LINK = '#000000'; // black
  65. var MASTERS = '#FFFFFF'; // white
  66. //var MASTERS = '#B56CFF'; // purple
  67.  
  68. var NEW_NOTQUAL_BODY = '#A9AAA4'; // dark grey
  69.  
  70. var COMM_WEIGHT = 1;
  71. var PAY_WEIGHT = 3;
  72. var FAIR_WEIGHT = 3;
  73. var FAST_WEIGHT = 1;
  74.  
  75. var CHECKPOINT_COLOR = "#000000"; // black
  76. //var CHECKPOINT_COLOR = "#00AAAA"; // dark green-blue
  77. var CHECKPOINT_MESSAGE = "CHECKPOINT REACHED!";
  78. //var CHECKPOINT_MESSAGE = "YOU SHALL NOT PASS!";
  79.  
  80. var SHOW_ALL_DETAILS = true;
  81.  
  82. /**********************************************************************/
  83.  
  84. // URLs for testing, learning parameters
  85. //http://api.turkopticon.istrack.in/multi-attrs.php?ids=ABSF8UXFZYEK6
  86. //http://data.istrack.in/turkopticon.php?data=2.57,2.31,2.89,2.71
  87.  
  88.  
  89. //var API_BASE = 'http://turkopticon.ucsd.edu/api/';
  90. //var API_MULTI_ATTRS_URL = API_BASE + 'multi-attrs.php?ids=';
  91. // secure http proxy to prevent mixed content problems
  92. var API_PROXY_BASE = 'https://api.turkopticon.istrack.in/';
  93. var API_MULTI_ATTRS_URL = API_PROXY_BASE + 'multi-attrs.php?ids=';
  94. var REVIEWS_BASE = 'http://turkopticon.ucsd.edu/';
  95. var HIT_GROUPS_BASE_LINK = '/mturk/searchbar?selectedSearchType=hitgroups&requesterId=';
  96.  
  97. var NOTQUAL_BODY = '#F1F3EB';
  98.  
  99. var qual_checkbox;
  100. var notqual_checkbox;
  101. var green_checkbox;
  102. var yellow_checkbox;
  103. var orange_checkbox;
  104. var red_checkbox;
  105. var blue_checkbox;
  106. var $parent_tables;
  107.  
  108. function process_TO_data(requester_data)
  109. {
  110. var average = 0;
  111. var comm_rnd = 0;
  112. var pay_rnd = 0;
  113. var fair_rnd = 0;
  114. var fast_rnd = 0;
  115. var reviews = 0;
  116. var tos = 0;
  117.  
  118. // after the API update, this if isn't necessary. leaving it in until
  119. // sure API is stable
  120. if (requester_data)
  121. {
  122. var comm = requester_data.attrs.comm;
  123. var pay = requester_data.attrs.pay;
  124. var fair = requester_data.attrs.fair;
  125. var fast = requester_data.attrs.fast;
  126. var sum = 0;
  127. var divisor = 0;
  128.  
  129. if (comm > 0)
  130. {
  131. sum += COMM_WEIGHT*comm;
  132. divisor += COMM_WEIGHT;
  133. }
  134. if (pay > 0)
  135. {
  136. sum += PAY_WEIGHT*pay;
  137. divisor += PAY_WEIGHT;
  138. }
  139. if (fair > 0)
  140. {
  141. sum += FAIR_WEIGHT*fair;
  142. divisor += FAIR_WEIGHT;
  143. }
  144. if (fast > 0)
  145. {
  146. sum += FAST_WEIGHT*fast;
  147. divisor += FAST_WEIGHT;
  148. }
  149. if (divisor > 0)
  150. {
  151. average = sum/divisor;
  152. }
  153.  
  154. comm_rnd = Math.round(comm*4)/4;
  155. pay_rnd = Math.round(pay*4)/4;
  156. fair_rnd = Math.round(fair*4)/4;
  157. fast_rnd = Math.round(fast*4)/4;
  158. if (requester_data.reviews)
  159. {
  160. reviews = requester_data.reviews;
  161. }
  162. if (requester_data.tos_flags)
  163. {
  164. tos = requester_data.tos_flags;
  165. }
  166. }
  167.  
  168. comm_rnd = comm_rnd.toFixed(2);
  169. pay_rnd = pay_rnd.toFixed(2);
  170. fair_rnd = fair_rnd.toFixed(2);
  171. fast_rnd = fast_rnd.toFixed(2);
  172.  
  173. return {comm_rnd:comm_rnd, pay_rnd:pay_rnd, fair_rnd:fair_rnd, fast_rnd:fast_rnd, reviews:reviews, tos:tos, average:average};
  174. }
  175.  
  176. function determine_color(rating)
  177. {
  178. // The lowest rating that can be given is a 1.
  179. // green is (4,5]
  180. // yellow is (3,4]
  181. // orange is (2,3]
  182. // red is (1,2]
  183. // blue is 0 (no rating)
  184. // (0,1) is no man's land but I set the lower bound for red to 0 to agree with data.istrack.in
  185.  
  186. var color = BLUE;
  187.  
  188. if (rating > 4)
  189. {
  190. color = GREEN;
  191. }
  192. else if (rating > 3)
  193. {
  194. color = YELLOW;
  195. }
  196. else if (rating > 2 )
  197. {
  198. color = ORANGE;
  199. }
  200. else if (rating > 0)
  201. {
  202. color = RED;
  203. }
  204.  
  205. return color;
  206. }
  207.  
  208. function show_hide_color(color)
  209. {
  210. if (notqual_checkbox.checked == true)
  211. {
  212. var $color_subset_tables = $parent_tables.filter('[title_color='+color+'][qualified_for="false"][hideable!=false]');
  213. }
  214. else
  215. {
  216. var $color_subset_tables = $parent_tables.filter('[title_color='+color+'][hideable!=false]');
  217. }
  218. switch(color)
  219. {
  220. case GREEN:
  221. {
  222. GM_setValue('green_checkbox_checked', green_checkbox.checked);
  223. if (green_checkbox.checked == false)
  224. {
  225. $color_subset_tables.each(function()
  226. {
  227. $(this).hide();
  228. });
  229. }
  230. else
  231. {
  232. $color_subset_tables.each(function()
  233. {
  234. $(this).show();
  235. });
  236. }
  237. break;
  238. }
  239. case YELLOW:
  240. {
  241. GM_setValue('yellow_checkbox_checked', yellow_checkbox.checked);
  242. if (yellow_checkbox.checked == false)
  243. {
  244. $color_subset_tables.each(function()
  245. {
  246. $(this).hide();
  247. });
  248. }
  249. else
  250. {
  251. $color_subset_tables.each(function()
  252. {
  253. $(this).show();
  254. });
  255. }
  256. break;
  257. }
  258. case ORANGE:
  259. {
  260. GM_setValue('orange_checkbox_checked', orange_checkbox.checked);
  261. if (orange_checkbox.checked == false)
  262. {
  263. $color_subset_tables.each(function()
  264. {
  265. $(this).hide();
  266. });
  267. }
  268. else
  269. {
  270. $color_subset_tables.each(function()
  271. {
  272. $(this).show();
  273. });
  274. }
  275. break;
  276. }
  277. case RED:
  278. {
  279. GM_setValue('red_checkbox_checked', red_checkbox.checked);
  280. if (red_checkbox.checked == false)
  281. {
  282. $color_subset_tables.each(function()
  283. {
  284. $(this).hide();
  285. });
  286. }
  287. else
  288. {
  289. $color_subset_tables.each(function()
  290. {
  291. $(this).show();
  292. });
  293. }
  294. break;
  295. }
  296. case BLUE:
  297. {
  298. GM_setValue('blue_checkbox_checked', blue_checkbox.checked);
  299. if (blue_checkbox.checked == false)
  300. {
  301. $color_subset_tables.each(function()
  302. {
  303. $(this).hide();
  304. });
  305. }
  306. else
  307. {
  308. $color_subset_tables.each(function()
  309. {
  310. $(this).show();
  311. });
  312. }
  313. break;
  314. }
  315. }
  316. }
  317.  
  318. function show_hide_all_colors()
  319. {
  320. show_hide_color(GREEN);
  321. show_hide_color(YELLOW);
  322. show_hide_color(ORANGE);
  323. show_hide_color(RED);
  324. show_hide_color(BLUE);
  325. }
  326.  
  327. function show_hide_qual()
  328. {
  329. GM_setValue('notqual_checkbox_checked', notqual_checkbox.checked);
  330. if (notqual_checkbox.checked == true)
  331. {
  332. $parent_tables.filter('[qualified_for="true"][hideable!=false]').each(function()
  333. {
  334. $(this).hide();
  335. });
  336. }
  337. else
  338. {
  339. show_hide_all_colors();
  340. }
  341. }
  342.  
  343. function set_checkpoint(e)
  344. {
  345. var caller = e.target || e.srcElement;
  346.  
  347. if (caller.checked)
  348. {
  349. var d = new Date();
  350. GM_setValue(caller.name+'_checked', caller.checked);
  351. GM_setValue(caller.name+'_date', '['+d.toLocaleDateString()+'] ');
  352. }
  353. else
  354. {
  355. GM_deleteValue(caller.name+'_checked');
  356. GM_deleteValue(caller.name+'_date');
  357. }
  358. }
  359.  
  360. function create_colored_checkboxes()
  361. {
  362. var checkbox_div = document.createElement('DIV');
  363. var notqual_div = document.createElement('DIV');
  364. var green_div = document.createElement('DIV');
  365. var yellow_div = document.createElement('DIV');
  366. var orange_div = document.createElement('DIV');
  367. var red_div = document.createElement('DIV');
  368. var blue_div = document.createElement('DIV');
  369. notqual_div.style.cssText = 'display:inline-block; background-color: '+NEW_NOTQUAL_BODY+';'
  370. green_div.style.cssText = 'display:inline-block; background-color: '+GREEN+';'
  371. yellow_div.style.cssText = 'display:inline-block; background-color: '+YELLOW+';'
  372. orange_div.style.cssText = 'display:inline-block; background-color: '+ORANGE+';'
  373. red_div.style.cssText = 'display:inline-block; background-color: '+RED+';'
  374. blue_div.style.cssText = 'display:inline-block; background-color: '+BLUE+';'
  375.  
  376. notqual_checkbox = document.createElement('INPUT');
  377. green_checkbox = document.createElement('INPUT');
  378. yellow_checkbox = document.createElement('INPUT');
  379. orange_checkbox = document.createElement('INPUT');
  380. red_checkbox = document.createElement('INPUT');
  381. blue_checkbox = document.createElement('INPUT');
  382. notqual_checkbox.type = 'checkbox';
  383. green_checkbox.type = 'checkbox';
  384. yellow_checkbox.type = 'checkbox';
  385. orange_checkbox.type = 'checkbox';
  386. red_checkbox.type = 'checkbox';
  387. blue_checkbox.type = 'checkbox';
  388. notqual_checkbox.checked = GM_getValue('notqual_checkbox_checked', false);
  389. green_checkbox.checked = GM_getValue('green_checkbox_checked', true);
  390. yellow_checkbox.checked = GM_getValue('yellow_checkbox_checked', true);
  391. orange_checkbox.checked = GM_getValue('orange_checkbox_checked', true);
  392. red_checkbox.checked = GM_getValue('red_checkbox_checked', true);
  393. blue_checkbox.checked = GM_getValue('blue_checkbox_checked', true);
  394. notqual_checkbox.name = 'notqual_checkbox';
  395. green_checkbox.name = 'green_checkbox';
  396. yellow_checkbox.name = 'yellow_checkbox';
  397. orange_checkbox.name = 'orange_checkbox';
  398. red_checkbox.name = 'red_checkbox';
  399. blue_checkbox.name = 'blue_checkbox';
  400. notqual_checkbox.title = 'Only show HITs for which you are not qualified';
  401. green_checkbox.title = 'Show/Hide green';
  402. yellow_checkbox.title = 'Show/Hide yellow';
  403. orange_checkbox.title = 'Show/Hide orange';
  404. red_checkbox.title = 'Show/Hide red';
  405. blue_checkbox.title = 'Show/Hide no TO';
  406. notqual_checkbox.addEventListener('click', show_hide_qual);
  407. green_checkbox.addEventListener('click', function(){show_hide_color(GREEN);});
  408. yellow_checkbox.addEventListener('click', function(){show_hide_color(YELLOW);});
  409. orange_checkbox.addEventListener('click', function(){show_hide_color(ORANGE);});
  410. red_checkbox.addEventListener('click', function(){show_hide_color(RED);});
  411. blue_checkbox.addEventListener('click', function(){show_hide_color(BLUE);});
  412.  
  413. notqual_div.appendChild(notqual_checkbox);
  414. green_div.appendChild(green_checkbox);
  415. yellow_div.appendChild(yellow_checkbox);
  416. orange_div.appendChild(orange_checkbox);
  417. red_div.appendChild(red_checkbox);
  418. blue_div.appendChild(blue_checkbox);
  419. checkbox_div.align = 'center';
  420. checkbox_div.appendChild(notqual_div);
  421. checkbox_div.appendChild(green_div);
  422. checkbox_div.appendChild(yellow_div);
  423. checkbox_div.appendChild(orange_div);
  424. checkbox_div.appendChild(red_div);
  425. checkbox_div.appendChild(blue_div);
  426. return checkbox_div;
  427. }
  428.  
  429. //$(document).ready(function()
  430. //{
  431. var is_HIT = $('input[type="hidden"][name="isAccepted"]').length > 0;
  432. if (is_HIT)
  433. {
  434. // not on a search page so quit
  435. return;
  436. }
  437.  
  438. // change visited link color to make it easier to differentiate from unvisited link
  439. // code snippet from http://stackoverflow.com/questions/7030289/how-to-set-link-visited-color-in-jquery
  440. var visited_link_styling = '<style> a:visited {color:'+VISITED_LINK+';} </style>';
  441. $("head").append(visited_link_styling);
  442. // end snippet
  443. var checkbox_div = create_colored_checkboxes();
  444. $("table[cellspacing='0'][cellpadding='0'][border='0'][style='margin:5px; clear:both;']").eq(1).after(checkbox_div);
  445.  
  446. if (SHOW_ALL_DETAILS)
  447. {
  448. // click 'Show all details'
  449. $(window).load(function(){$('a[id="expandall"][class="footer_links"][href="#"]:contains("Show all details")').get(0).click();});
  450. }
  451.  
  452. // change color of HITs not qualified for to make it easier to differentiate
  453. $('[bgcolor="'+NOTQUAL_BODY+'"]').each(function(){
  454. $(this).attr('bgcolor',$(this).attr('bgcolor').replace(NOTQUAL_BODY, NEW_NOTQUAL_BODY));
  455. });
  456.  
  457. var url = API_MULTI_ATTRS_URL;
  458. var requester_IDs = new Array();
  459. $parent_tables = $('table[width="100%"][cellspacing="0"][cellpadding="0"][border="0"][height="100%"]');
  460.  
  461. $parent_tables.each(function()
  462. {
  463. var requester_ID_link = $(this).find('a[href^="'+HIT_GROUPS_BASE_LINK+'"]').attr('href');
  464. requester_IDs.push(requester_ID_link.replace(HIT_GROUPS_BASE_LINK,''));
  465. });
  466.  
  467. // code snippet from http://stackoverflow.com/questions/5381621/jquery-function-to-get-all-unique-elements-from-an-array
  468. requester_IDs = requester_IDs.filter(function(itm,i,a)
  469. {
  470. return i==a.indexOf(itm);
  471. });
  472. // end snippet
  473.  
  474. for (var i = 0; i<requester_IDs.length-1; i++)
  475. {
  476. url += requester_IDs[i] + ','
  477. }
  478. url += requester_IDs[i];
  479. GM_xmlhttpRequest(
  480. {
  481. method: "GET",
  482. url: url,
  483. onload: function (results)
  484. {
  485. var rdata = $.parseJSON(results.responseText);
  486. $parent_tables.each(function()
  487. {
  488. var requester_ID_link = $(this).find('a[href^="'+HIT_GROUPS_BASE_LINK+'"]').attr('href');
  489. var requester_ID = requester_ID_link.replace(HIT_GROUPS_BASE_LINK,'');
  490. var title_row = $(this).find("tr").eq(1);
  491. var link_bgcolor = $(this).find('td[width="100%"][valign="middle"][height="20"][align="left"]').attr('bgcolor');
  492.  
  493. var pdata = process_TO_data(rdata[requester_ID]);
  494. var title_color = determine_color(pdata.average);
  495. var qualified_for = !($(this).find('a[href^="/mturk/notqualified?"]').length > 0);
  496.  
  497. $(this).attr('title_color', title_color);
  498. $(this).attr('qualified_for', qualified_for);
  499.  
  500. //var $title_line = $(this).find('td[width="100%"][valign="middle"][height="20"][align="left"]');
  501. //$title_line.css('background-color', title_color);
  502. $(this).find('td[valign="middle"][nowrap=""][align="left"]').css('background-color', title_color);
  503. $(this).find('td[valign="middle"][align="left"]').css('background-color', title_color);
  504. $(this).find('td[width="100%"][valign="middle"][nowrap=""][align="right"]').css('background-color', title_color);
  505. //$(this).find('a[href^="/mturk/preview?groupId="]').css('background-color', link_bgcolor);
  506.  
  507. var link_href = REVIEWS_BASE + requester_ID;
  508. var link_text = pdata.reviews + ((pdata.reviews != 1) ? ' reviews ' : ' review ');
  509. //link_text = '['+link_text + '|comm: '+pdata.comm_rnd+'|pay: '+pdata.pay_rnd+'|fair: '+pdata.fair_rnd+'|fast: '+pdata.fast_rnd+'|tos: '+pdata.tos+']';
  510. link_text = '['+link_text + '|pay: '+pdata.pay_rnd+'|fair: '+pdata.fair_rnd+'|comm: '+pdata.comm_rnd+'|fast: '+pdata.fast_rnd+'|tos: '+pdata.tos+']';
  511. var link = '<a href="'+link_href+'" target="_blank">'+link_text+'</a>&nbsp;';
  512. title_row.after('<tr><td width="1" valign="middle" bgcolor="#336699" align="center"></td><td width="18" valign="middle" bgcolor="'+link_bgcolor+'" align="center"></td><td width="100%" valign="top" bgcolor="'+title_color+'" align="right">'+link+'</td><td width="8" valign="middle" bgcolor="'+link_bgcolor+'" align="center"></td><td width="1" valign="middle" bgcolor="#336699" align="center"></td></tr>');
  513.  
  514. // after the API update, this if isn't necessary. leaving it in until
  515. // sure API is stable
  516. var pay = 0;
  517. if (rdata[requester_ID])
  518. {
  519. pay = rdata[requester_ID].attrs.pay;
  520. }
  521. var pay_color = determine_color(pay);
  522. //var pay_color = determine_color(rdata[requester_ID].attrs.pay);
  523. $(this).find('span[class="reward"]').css('background-color', pay_color);
  524.  
  525. // highlight Masters HITs title and link
  526. var is_masters = $(this).find('td[style="padding-right: 2em; white-space: nowrap;"]:contains("Masters")').length > 0;
  527. if (is_masters)
  528. {
  529. $(this).find('td[valign="middle"][nowrap=""][align="left"]').css('background-color', MASTERS);
  530. $(this).find('a[href^="/mturk/preview?groupId="]').css('background-color', MASTERS);
  531. }
  532.  
  533.  
  534. // create checkpoints
  535. var $groupId_link = $(this).find('a[href^="/mturk/preview?groupId="]');
  536. if ($groupId_link.length > 0)
  537. {
  538. var checkbox = document.createElement('INPUT');
  539. checkbox.type = 'checkbox';
  540. checkbox.name = $groupId_link.attr('href').slice(23); // groupId
  541. checkbox.title = 'Set a checkpoint to help remember a HIT you\'ve seen before.\nUseful when browsing by HIT Creation Date.';
  542. checkbox.checked = GM_getValue(checkbox.name+'_checked', false);
  543. checkbox.addEventListener('click', set_checkpoint);
  544. checkbox.style.cssText ='vertical-align:middle;';
  545. $groupId_link.after(checkbox);
  546. // mark checkpoints
  547. if (checkbox.checked == true)
  548. {
  549. var checkpoint_date = GM_getValue(checkbox.name+'_date');
  550. $groupId_link.text(checkpoint_date+CHECKPOINT_MESSAGE);
  551. $(this).attr('hideable', false);
  552. $(this).css('border', '50px solid '+CHECKPOINT_COLOR);
  553. }
  554. }
  555.  
  556. });
  557. show_hide_all_colors();
  558. show_hide_qual();
  559. }
  560. });
  561. //});