CH Plaintext HIT Export

Export HIT information in multi-line plain text format.

目前为 2015-06-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name CH Plaintext HIT Export
  3. // @description Export HIT information in multi-line plain text format.
  4. // @version 1.6c
  5. // @include https://www.mturk.com/mturk/findhits*
  6. // @include https://www.mturk.com/mturk/viewhits*
  7. // @include https://www.mturk.com/mturk/sorthits*
  8. // @include https://www.mturk.com/mturk/searchbar*selectedSearchType=hitgroups*
  9. // @include https://www.mturk.com/mturk/viewsearchbar*selectedSearchType=hitgroups*
  10. // @include https://www.mturk.com/mturk/sortsearchbar*HITGroup*
  11. // @include https://www.mturk.com/mturk/preview*
  12. // @grant GM_setClipboard
  13. // @author Cristo + clickhappier
  14. // @namespace mturkgrind
  15. // ==/UserScript==
  16.  
  17.  
  18. // based on 'IRC Export (reformatted output mod)': https://greasyfork.org/en/scripts/6254-irc-export-reformatted-output-mod
  19.  
  20.  
  21. var caps = document.getElementsByClassName('capsulelink');
  22. for (var c = 0; c < caps.length/2; c++){
  23. button = document.createElement('button');
  24. button.setAttribute("place",c);
  25. button.textContent = 'TXT';
  26. button.style.height = '14px';
  27. button.style.width = '30px';
  28. button.style.fontSize = '8px';
  29. button.style.border = '1px solid';
  30. button.style.padding = '0px';
  31. button.style.backgroundColor = 'transparent';
  32. button.title = 'Click to save Hit information to your clipboard';
  33. button.addEventListener("click", display, false);
  34. document.getElementById('capsule'+c+'-0').parentNode.appendChild(button);
  35. }
  36.  
  37. function getTO(f){
  38. var toComp = [];
  39. var toUrl = 'https://mturk-api.istrack.in/multi-attrs.php?ids='+f;
  40. var toUrl2 = 'https://turkopticon.ucsd.edu/api/multi-attrs.php?ids='+f;
  41. requestTO = new XMLHttpRequest();
  42. try{ // first try Miku's TO mirror server (istrack.in)
  43. requestTO.onreadystatechange = function () {
  44. if ((requestTO.readyState ===4) && (requestTO.status ===200)) {
  45. if (requestTO.responseText.split(':').length > 2) {
  46. var toInfo = requestTO.responseText.split('{')[3].split('}')[0].split(',');
  47. for (var t = 0; t < 4; t++) {
  48. var arrTo = toInfo[t].split(':');
  49. toComp.push(arrTo[1].substring(1,4));
  50. }
  51. }
  52. else { toComp = ['-','-','-','-']; }
  53. }
  54. };
  55. requestTO.open('GET', toUrl, false);
  56. requestTO.send(null);
  57. return toComp;
  58. }
  59. catch(err){ // if mirror unavailable, try main TO server
  60. try{
  61. requestTO.onreadystatechange = function () {
  62. if ((requestTO.readyState ===4) && (requestTO.status ===200)) {
  63. if (requestTO.responseText.split(':').length > 2) {
  64. var toInfo = requestTO.responseText.split('{')[3].split('}')[0].split(',');
  65. for (var t = 0; t < 4; t++) {
  66. var arrTo = toInfo[t].split(':');
  67. toComp.push(arrTo[1].substring(1,4));
  68. }
  69. }
  70. else { toComp = ['-','-','-','-']; }
  71. }
  72. };
  73. requestTO.open('GET', toUrl2, false);
  74. requestTO.send(null);
  75. return toComp;
  76. }
  77. catch(err){ // if both unavailable, return 'na's
  78. toComp = ['na','na','na','na'];
  79. return toComp;
  80. }
  81. }
  82. }
  83.  
  84. // output display box
  85. var txtexportdiv = document.createElement('div');
  86. var txtexporttextarea = document.createElement('textarea');
  87. txtexportdiv.style.position = 'fixed';
  88. txtexportdiv.style.width = '500px';
  89. txtexportdiv.style.height = '255px';
  90. txtexportdiv.style.left = '50%';
  91. txtexportdiv.style.right = '50%';
  92. txtexportdiv.style.margin = '-250px 0px 0px -250px';
  93. txtexportdiv.style.top = '300px';
  94. txtexportdiv.style.padding = '5px';
  95. txtexportdiv.style.border = '2px';
  96. txtexportdiv.style.backgroundColor = 'black';
  97. txtexportdiv.style.color = 'white';
  98. txtexportdiv.style.zIndex = '100';
  99. txtexportdiv.setAttribute('id','txtexport_div');
  100. txtexportdiv.style.display = 'none';
  101. txtexporttextarea.style.padding = '2px';
  102. txtexporttextarea.style.width = '500px';
  103. txtexporttextarea.style.height = '230px';
  104. txtexporttextarea.title = 'Plaintext Export Output';
  105. txtexporttextarea.setAttribute('id','txtexport_text');
  106. txtexportdiv.textContent = 'Plaintext Export: Press Ctrl+C to (re-)copy to clipboard. Click textarea to close.';
  107. txtexportdiv.style.fontSize = '12px';
  108. txtexportdiv.appendChild(txtexporttextarea);
  109. document.body.insertBefore(txtexportdiv, document.body.firstChild);
  110. txtexporttextarea.addEventListener("click", function(){ txtexportdiv.style.display = 'none'; }, false);
  111.  
  112.  
  113. function display(e){
  114. var theButton = e.target;
  115. theButton.style.backgroundColor = '#CC0000';
  116. var capHand = document.getElementById('capsule'+theButton.getAttribute("place")+'-0');
  117. var tBodies = capHand.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
  118. var capReq = tBodies.getElementsByClassName('requesterIdentity')[0].textContent;
  119. var capReqId = tBodies.getElementsByClassName('requesterIdentity')[0].parentNode.href.split('requesterId=')[1];
  120. // if on certain types of searches when logged out, more infernal Amazon-meddling may add a useless &state= value to the end of the requester link
  121. if ( capReqId.indexOf('&state=') > -1 )
  122. {
  123. capReqId = capReqId.split('&state')[0];
  124. }
  125. var capTitle = capHand.textContent.trim();
  126. capTitle = capTitle.replace(/<(\w+)[^>]*>.*<\/\1>/gi, "").trim(); // addition to strip html tags and their contents, appearing inside the title link (re 10-20-2014 appearance of "<span class="tags"></span>")
  127.  
  128. var capGId = 'unavailable'; // handle logged-out export requests for HITs with no preview/notqualified links
  129. // if hit has a preview or notqualified link
  130. if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href !== '' )
  131. {
  132. // if this is a preview link
  133. if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.indexOf('preview') > -1 )
  134. {
  135. capGId = capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.split('=')[1];
  136. }
  137. // if this is a notqualified link
  138. else if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.indexOf('notqualified') > -1 )
  139. {
  140. capGId = capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.split('=')[1];
  141. // Amazon messed up the notqualified links, now looking like https://www.mturk.com/mturk/notqualified?hitGroupId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX&hitId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX ; this and the above split happening on = instead of a specific value address that
  142. capGId = capGId.replace("&hitId", "").replace("&hitGroupId", ""); // added a removal of hitGroupId too since Amazon flipped the order of these on 6/2/15
  143. }
  144. // if this is a requestqualification link we shouldn't be on, but are anyway because of stuff Amazon screwed with on 6/2/15
  145. else if ( capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.href.indexOf('requestqualification') > -1 )
  146. {
  147. // go to the next link, the "(why?)" notqualified link instead
  148. capGId = capHand.parentNode.parentNode.getElementsByClassName('capsulelink')[1].firstChild.nextSibling.nextElementSibling.href.split('=')[1];
  149. // Amazon messed up the notqualified links, now looking like https://www.mturk.com/mturk/notqualified?hitGroupId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX&hitId=3ID43DSF4IQ1X8LO308D15ZSD5J5GX ; this and the above split happening on = instead of a specific value address that
  150. capGId = capGId.replace("&hitId", "").replace("&hitGroupId", ""); // added a removal of hitGroupId too since Amazon flipped the order of these on 6/2/15
  151. }
  152. }
  153. var capRew = tBodies.getElementsByClassName('reward')[0].textContent;
  154.  
  155. var capTime = tBodies.getElementsByClassName('capsule_field_text')[2].textContent;
  156.  
  157. var capAvailable = tBodies.getElementsByClassName('capsule_field_text')[4].textContent;
  158.  
  159. var qualList = document.getElementById('capsule'+theButton.getAttribute("place")+'target').getElementsByTagName('tbody')[2];
  160. if ( document.location.href.indexOf('?last_hits_previewed') > -1 ) { qualList = document.getElementById('capsule'+theButton.getAttribute("place")+'target').getElementsByTagName('tbody')[1]; }
  161. var qualColl = qualList.getElementsByTagName('td');
  162. var qualStart = 3;
  163. if ( document.getElementById('lnkWorkerSignin') ) { qualStart = 1; } // handle logged-out export requests - difference in qual table coding
  164. if ( document.location.href.indexOf('?last_hits_previewed') > -1 ) { qualStart = 2; }
  165. var masterStat = '';
  166. for ( var m = qualStart; m < qualColl.length; m++ ) {
  167. if ( qualColl[m].textContent.indexOf('Masters') > -1 ) {
  168. masterStat = 'MASTERS ';
  169. }
  170. }
  171. var capUrl = 'https://www.mturk.com/mturk/preview?groupId='+capGId;
  172. var capReqUrl = 'https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId='+capReqId;
  173. var hitLinkUnav = '';
  174. if ( capGId == 'unavailable' ) { capUrl = capReqUrl; hitLinkUnav = " (preview link unavailable)"; } // handle logged-out export requests for HITs with no preview/notqualified links
  175. var toLink = 'http://turkopticon.ucsd.edu/'+capReqId;
  176. var capToStats = getTO(capReqId);
  177.  
  178.  
  179. // additions for plaintext export:
  180. function DST() { // check if daylight savings time should be adjusted for, from http://www.mresoftware.com/simpleDST.htm
  181. var today = new Date();
  182. var yr = today.getFullYear();
  183. var dst_start = new Date("March 14, "+yr+" 02:00:00"); // 2nd Sunday in March can't occur after the 14th
  184. var dst_end = new Date("November 07, "+yr+" 02:00:00"); // 1st Sunday in November can't occur after the 7th
  185. var day = dst_start.getDay(); // day of week of 14th
  186. dst_start.setDate(14-day); // Calculate 2nd Sunday in March of this year
  187. day = dst_end.getDay(); // day of the week of 7th
  188. dst_end.setDate(7-day); // Calculate first Sunday in November of this year
  189. if (today >= dst_start && today < dst_end) { //does today fall inside of DST period?
  190. return true; //if so then return true
  191. }
  192. return false; //if not then return false
  193. }
  194.  
  195. var currentDate = new Date();
  196. var utc = currentDate.getTime() + (currentDate.getTimezoneOffset() * 60000); // http://www.techrepublic.com/article/convert-the-local-time-to-another-time-zone-with-this-javascript/
  197. var offset = '';
  198. if ( DST() == true ) { offset = "-7"; } else { offset = "-8"; } // adjust Pacific Time's UTC offset for daylight savings time - http://stackoverflow.com/questions/8207655/how-to-get-time-of-specific-timezone-using-javascript/8207708#8207708
  199. var amazonDate = new Date(utc + (3600000*offset));
  200. var month = amazonDate.getMonth() + 1;
  201. var day = amazonDate.getDate();
  202. var year = amazonDate.getFullYear();
  203. var hours = amazonDate.getHours();
  204. if (hours < 10) { hours = '0' + hours; } // http://stackoverflow.com/questions/6838197/get-local-date-string-and-time-string/6838658#6838658
  205. var minutes = amazonDate.getMinutes();
  206. if (minutes < 10) { minutes = '0' + minutes; }
  207. var dateStr = month + "/" + day + "/" + year + " " + hours + ":" + minutes + " PT";
  208. var capDesc = '"' + tBodies.getElementsByClassName('capsule_field_text')[5].textContent.trim().replace(/(\t)+/g,' ').replace(/(\n)+/g,' ').replace(/(\r)+/g,' ').replace(/( )+/g,' ').replace(/(\s)+/g,' ') + '"';
  209. if (capDesc == '""') { capDesc = "none"; }
  210.  
  211. var capKeywords = "";
  212. if ( document.location.href.indexOf('?last_hits_previewed') > -1 ) { capKeywords = "unavailable (exported from Last HITs Previewed)"; }
  213. else { capKeywords = '"' + tBodies.getElementsByClassName('capsule_field_text')[6].textContent.trim().replace(/(\t)+/g,' ').replace(/(\n)+/g,' ').replace(/(\r)+/g,' ').replace(/( )+/g,' ').replace(/(\s)+/g,' ') + '"'; }
  214. if (capKeywords == '""' || capKeywords == '') { capKeywords = "none"; }
  215.  
  216. var qualStr = "";
  217. for ( var q = qualStart; q < qualColl.length; q++ ) {
  218. if ( ( (qualColl[q].textContent.indexOf('is') > -1) || (qualColl[q].textContent.indexOf('has') > -1) ) && (qualColl[q].textContent.indexOf('You meet this') < 0) && (qualColl[q].textContent.indexOf('Contact the Requester') < 0) ) {
  219. if (qualStr != "") { qualStr += ' '; }
  220. qualStr += qualColl[q].textContent.trim().replace(/(\t)+/g,' ').replace(/(\n)+/g,' ').replace(/(\r)+/g,' ').replace(/( )+/g,' ').replace(/(\s)+/g,' ') + ' \r\n';
  221. }
  222. }
  223. if (qualStr == "") { qualStr = "none \r\n"; }
  224.  
  225.  
  226. var exString = dateStr + ' \r\n'
  227. + masterStat + 'HIT: ' + capTitle + ' - ' + capUrl + hitLinkUnav + ' \r\n'
  228. + 'Requester: ' + capReq + ' - ' + capReqUrl + ' \r\n'
  229. + 'TO Ratings: ' + 'Pay='+capToStats[1] + ' Fair='+capToStats[2] + ' Comm='+capToStats[0] + ' Speed='+capToStats[3] + ' - ' + toLink + ' \r\n'
  230. + 'Time Allotted: ' + capTime + ' \r\n'
  231. + 'Reward: ' + capRew + ' \r\n'
  232. + 'HITs Available: ' + capAvailable + ' \r\n'
  233. + 'Description: ' + capDesc + ' \r\n'
  234. + 'Keywords: ' + capKeywords + ' \r\n'
  235. + 'Qualifications: ' + qualStr + ' \r\n' ;
  236. if (GM_setClipboard) { GM_setClipboard(exString); }
  237. window.setTimeout(function(){ theButton.style.backgroundColor = 'transparent'; }, 500);
  238. txtexporttextarea.textContent = exString;
  239. txtexportdiv.style.display = 'block';
  240. txtexporttextarea.select();
  241. }