MTurk HIT DataBase

Extended ability to search HITs you have worked on and other useful tools (CSV export/import, requester notes, requester block, pending/projected earnings)

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

  1. // ==UserScript==
  2. // @name MTurk HIT DataBase
  3. // @namespace https://greasyfork.org/users/710
  4. // @description Extended ability to search HITs you have worked on and other useful tools (CSV export/import, requester notes, requester block, pending/projected earnings)
  5. // @include https://www.mturk.com/mturk/searchbar*
  6. // @include https://www.mturk.com/mturk/findhits*
  7. // @include https://www.mturk.com/mturk/viewhits*
  8. // @include https://www.mturk.com/mturk/viewsearchbar*
  9. // @include https://www.mturk.com/mturk/sortsearchbar*
  10. // @include https://www.mturk.com/mturk/sorthits*
  11. // @include https://www.mturk.com/mturk/dashboard
  12. // @include https://www.mturk.com/mturk/preview?*
  13. // @version 1.9.5
  14. // @grant none
  15. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
  16. // @require http://code.highcharts.com/highcharts.js
  17. // @require https://greasyfork.org/scripts/2351-jsdiff/code/jsdiff.js?version=6256
  18. // @require https://greasyfork.org/scripts/2350-filesaver-js/code/filesaverjs.js?version=6255
  19. // ==/UserScript==
  20.  
  21. //
  22. // 2012-10-03 0.9.7: This is rewrite of MTurk Extended HIT Search (http://userscripts.org/scripts/show/146277)
  23. // with some extra features (and some missing for now: search by date).
  24. // It now uses IndexedDB (http://en.wikipedia.org/wiki/Indexed_Database_API)
  25. //
  26. // 2012-10-04 0.9.8: Improved use of indexes, check Pending Payment HITs
  27. // 0.9.9: Minor improvements
  28. //
  29. // 2012-10-04 0.10: Added date options
  30. //
  31. // 2012-10-07 0.11: Requester notes, bug fixes
  32. // 0.12: CSV export
  33. //
  34. // 2012-10-09 0.13: "Block" requesters or specific HITs
  35. //
  36. // 2012-10-10 0.14: Requester Overview, shows summary of all requesters in DB
  37. //
  38. // 2012-10-11 0.15: Blocked HITs are always on bottom of the page
  39. //
  40. // 2012-10-14 0.16: Requester Overview improvements
  41. //
  42. // 2012-10-17 0.17: Bug fixes and error checks
  43. //
  44. // 2012-10-18 0.18: Import HIT data from MTurk Extended HIT Search script
  45. //
  46. // 2012-10-21 0.19: Moved main interface to dashboard, show pending earnings on dashboard,
  47. // summary of all requesters with pending HITs.
  48. //
  49. // 2012-10-23 0.20: Added Turkopticon (http://turkopticon.differenceengines.com/) links to overview pages
  50. // 0.21: Fixed overview pages reward to include only 'Paid' and 'Approved - Pending Payment' HITs.
  51. //
  52. // 2012-10-28 0.22: Limited Auto Update.
  53. // 0.23: Minor improvements
  54. //
  55. // 2012-10-30 0.24: Projected earnings for today
  56. //
  57. // 2012-11-02 0.25: Smarter Auto Update
  58. //
  59. // 2012-11-03 0.26: GUI update
  60. //
  61. // 2012-11-05 0.30: Extra non-amazonian script monkeys
  62. //
  63. // 2012-11-06 0.31: Projected earnings progress bar
  64. //
  65. // 2012-11-08 0.32: Minor GUI fixes to look better on Chrome. Looks like it now works on stable Chrome!
  66. //
  67. // 2012-11-13 0.33: Time limits now work with Requester Overview
  68. //
  69. // 2012-11-15 0.34: Bug/compatibility fixes
  70. //
  71. // 2012-11-18 0.40: Daily Overview, update database to use YYYY-MM-DD date format.
  72. //
  73. // 2012-11-22 0.41: R and T button on HIT preview page. Auto-Approval time.
  74. //
  75. // 2012-11-30 0.42: Changes on MTurk pages. Status page in now on one page!
  76. //
  77. // 2012-12-02 1.0: Added @downloadURL and @updateURL
  78. //
  79. // 2012-12-06 1.1: Requester details.
  80. // Try to fetch few extra days at first update (not showing on status page).
  81. //
  82. // 2012-12-11 1.2: Import HITs from previously exported CSV-files.
  83. // Removed Extended HIT Search import.
  84. //
  85. // 2012-12-13 1.3: Fix CSV-import to put empty string instead if undefined if feedback is empty.
  86. //
  87. // 2012-12-14 1.4: Rewritten database update more properly.
  88. //
  89. // 2012-12-16 1.5: Fixed broken Auto Update (forgot to check that on pervious update).
  90. //
  91. // 2013-02-26 1.6: Fixed IDBTransactionModes for Chrome (note this breaks it for Firefox)
  92. //
  93. // 2013-02-27 1.7: Changed UI bars back to what they used to be.
  94. //
  95.  
  96. var DAYS_TO_FETCH = [];
  97. var DAYS_TO_FETCH_CHECK;
  98.  
  99. var HITStorage = {};
  100. var indexedDB = window.indexedDB || window.webkitIndexedDB ||
  101. window.mozIndexedDB;
  102. window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.mozIDBTransaction;
  103. window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange;
  104. HITStorage.IDBTransactionModes = { "READ_ONLY": "readonly", "READ_WRITE": "readwrite", "VERSION_CHANGE": "versionchange" };
  105. var IDBKeyRange = window.IDBKeyRange;
  106.  
  107. HITStorage.indexedDB = {};
  108. HITStorage.indexedDB = {};
  109. HITStorage.indexedDB.db = null;
  110.  
  111. HITStorage.indexedDB.onerror = function(e) {
  112. console.log(e);
  113. };
  114. var v = 4;
  115.  
  116. if (!localStorage["20150602fix"])
  117. {
  118. alert("ATTENTION!!!! Your hit database may not be functioning correctly. You will need to convert it, due to issues introduced by some code changes by mturk staff. This will involve a few steps. Please visit http://tinyurl.com/q58mxr6 and follow the instructions to convert. This message will disappear once the hitDB has been properly converted.");
  119. if (!localStorage["20150602alert"])
  120. localStorage["20150602alert"] = 1;
  121. else{
  122. var alertCount = parseInt(localStorage["20150602alert"]);
  123. localStorage["20150602alert"] = alertCount+1;
  124. }
  125. if (parseInt(localStorage["20150602alert"]) == 3)
  126. {
  127. alert("Alert has displayed 3 times, alert will not display anymore. Please convert your database if you have not done so already.");
  128. localStorage["20150602fix"] = 1;
  129. }
  130. }
  131.  
  132. HITStorage.indexedDB.create = function() {
  133.  
  134. var request = indexedDB.open("HITDB", v);
  135.  
  136. request.onupgradeneeded = function (e) {
  137. HITStorage.indexedDB.db = e.target.result;
  138. var db = HITStorage.indexedDB.db;
  139. var new_empty_db = false;
  140.  
  141. if(!db.objectStoreNames.contains("HIT")) {
  142. var store = db.createObjectStore("HIT", { keyPath: "hitId" });
  143.  
  144. store.createIndex("date", "date", { unique: false });
  145. store.createIndex("requesterName", "requesterName", { unique: false });
  146. store.createIndex("title", "title", { unique: false });
  147. store.createIndex("reward", "reward", { unique: false });
  148. store.createIndex("status", "status", { unique: false });
  149. store.createIndex("requesterId", "requesterId", { unique: false });
  150.  
  151. new_empty_db = true;
  152.  
  153. // At first update try to get few extra days that do not show on status page
  154. localStorage['HITDB TRY_EXTRA_DAYS'] = 'YES';
  155. }
  156. if(!db.objectStoreNames.contains("STATS")) {
  157. var store = db.createObjectStore("STATS", { keyPath: "date" });
  158. }
  159. if(!db.objectStoreNames.contains("NOTES")) {
  160. var store = db.createObjectStore("NOTES", { keyPath: "requesterId" });
  161. }
  162. if(!db.objectStoreNames.contains("BLOCKS")) {
  163. var store = db.createObjectStore("BLOCKS", { keyPath: "id", autoIncrement: true });
  164.  
  165. store.createIndex("requesterId", "requesterId", { unique: false });
  166. }
  167.  
  168. if (new_empty_db == false)
  169. {
  170. alert("HIT DataBase date format must be upgraded (MMDDYYYY => YYYY-MM-DD)\n" +
  171. "Please don't close or reload this page until it's done.\n" +
  172. "Press OK to start. This shouldn't take long. (few minutes max)" +
  173. "Sorry for the inconvenience.");
  174. HITStorage.update_date_format(true);
  175. }
  176. db.close();
  177. //alert("DataBase upgraded to version " + v + '!');
  178. }
  179.  
  180. request.onsuccess = function(e) {
  181. HITStorage.indexedDB.db = e.target.result;
  182. var db = HITStorage.indexedDB.db;
  183. db.close();
  184. };
  185.  
  186. request.onerror = HITStorage.indexedDB.onerror;
  187. }
  188.  
  189. HITStorage.indexedDB.addHIT = function(hitData) {
  190. // Temporary extra check
  191. if (hitData.date.indexOf('-') < 0)
  192. {
  193. alert('Wrong date format in addHIT()!');
  194. return;
  195. }
  196.  
  197. var request = indexedDB.open("HITDB", v);
  198. request.onsuccess = function(e) {
  199. HITStorage.indexedDB.db = e.target.result;
  200. var db = HITStorage.indexedDB.db;
  201. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  202. var store = trans.objectStore("HIT");
  203.  
  204. var request = store.put(hitData);
  205.  
  206. request.onsuccess = function(e) {
  207. db.close();
  208. };
  209.  
  210. request.onerror = function(e) {
  211. console.log("Error Adding: ", e);
  212. };
  213. };
  214. request.onerror = HITStorage.indexedDB.onerror;
  215. };
  216.  
  217. HITStorage.indexedDB.importHITs = function(hitData) {
  218. var hits = hitData.length;
  219. var label = document.getElementById('status_label');
  220.  
  221. var request = indexedDB.open("HITDB", v);
  222. request.onsuccess = function(e) {
  223. HITStorage.indexedDB.db = e.target.result;
  224. var db = HITStorage.indexedDB.db;
  225. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  226. var store = trans.objectStore("HIT");
  227.  
  228. putNextHIT();
  229.  
  230. function putNextHIT()
  231. {
  232. if (hitData.length > 0)
  233. {
  234. store.put(hitData.pop()).onsuccess = putNextHIT;
  235. label.innerHTML = progress_bar(((hits-hitData.length)/hits*50), 50, '█', '█', '#7fb448', 'grey') + ' (' + hitData.length + ')';
  236. }
  237. else
  238. {
  239. HITStorage.enable_inputs();
  240. HITStorage.update_status_label('Import done', 'green');
  241. db.close();
  242. }
  243. }
  244. };
  245. request.onerror = HITStorage.indexedDB.onerror;
  246. };
  247.  
  248. HITStorage.indexedDB.addHITs = function(hitData, day_to_fetch, days_to_update) {
  249. var hits = hitData.length;
  250. if (day_to_fetch)
  251. var label = document.getElementById('status_label');
  252.  
  253. var request = indexedDB.open("HITDB", v);
  254. request.onsuccess = function(e) {
  255. HITStorage.indexedDB.db = e.target.result;
  256. var db = HITStorage.indexedDB.db;
  257. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  258. var store = trans.objectStore("HIT");
  259.  
  260. putNextHIT();
  261.  
  262. function putNextHIT()
  263. {
  264. if (hitData.length > 0)
  265. {
  266. store.put(hitData.pop()).onsuccess = putNextHIT;
  267. if (day_to_fetch)
  268. label.innerHTML = 'Saving ' + day_to_fetch.date + ': ' + progress_bar(((hits-hitData.length)/hits*40), 40, '█', '█', '#7fb448', 'grey');
  269. }
  270. else
  271. {
  272. // move to next day
  273. if (day_to_fetch)
  274. {
  275. HITStorage.indexedDB.updateHITstats(day_to_fetch);
  276. setTimeout(function() { HITStorage.do_update(days_to_update); }, 2000);
  277. HITStorage.update_status_label('Please wait: script monkeys are taking naps ?', 'red');
  278. }
  279. db.close();
  280. }
  281. }
  282. };
  283. request.onerror = HITStorage.indexedDB.onerror;
  284. };
  285.  
  286.  
  287. HITStorage.indexedDB.updateHITstats = function(date)
  288. {
  289. // Temporary extra check
  290. if (date.date.indexOf('-') < 0)
  291. {
  292. alert('Wrong date format in updateHITstats()!');
  293. return;
  294. }
  295.  
  296. var request = indexedDB.open("HITDB", v);
  297. request.onsuccess = function(e) {
  298. HITStorage.indexedDB.db = e.target.result;
  299. var db = HITStorage.indexedDB.db;
  300. var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_WRITE);
  301. var store = trans.objectStore("STATS");
  302.  
  303. var request = store.put(date);
  304.  
  305. request.onsuccess = function(e) {
  306. db.close();
  307. };
  308.  
  309. request.onerror = function(e) {
  310. console.log("Error Adding: ", e);
  311. };
  312. };
  313. request.onerror = HITStorage.indexedDB.onerror;
  314. };
  315.  
  316. HITStorage.prepare_update_and_check_pending_payments = function()
  317. {
  318. var request = indexedDB.open("HITDB", v);
  319. request.onsuccess = function(e) {
  320. HITStorage.indexedDB.db = e.target.result;
  321. var db = HITStorage.indexedDB.db;
  322. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  323. var store = trans.objectStore("HIT");
  324. var index = store.index('status');
  325. var range = IDBKeyRange.only('Approved&nbsp;- Pending&nbsp;Payment');
  326.  
  327. index.openCursor(range).onsuccess = function(event) {
  328. var cursor = event.target.result;
  329. if (cursor && DAYS_TO_FETCH.length > 0)
  330. {
  331. for (var i=0; i<DAYS_TO_FETCH.length; i++)
  332. {
  333. if ( cursor.value.date == DAYS_TO_FETCH[i].date && cursor.value.reward>0 )
  334. {
  335. DAYS_TO_FETCH[i].pending_payments = true;
  336. }
  337. }
  338. cursor.continue();
  339. }
  340. else
  341. {
  342. if (DAYS_TO_FETCH.length>0) {
  343. db.close();
  344. HITStorage.update_status_label('Please wait: script monkeys are planning to fetch relevant status pages', 'red');
  345. setTimeout(function() { HITStorage.prepare_update(); }, 100);
  346. }
  347. else
  348. {
  349. db.close();
  350. HITStorage.update_done();
  351. }
  352. }
  353. };
  354. }
  355. };
  356.  
  357. // check that number of hits in DB matches what is available
  358. HITStorage.check_update = function()
  359. {
  360. var request = indexedDB.open("HITDB", v);
  361. request.onsuccess = function(e) {
  362. HITStorage.update_status_label('Please wait: checking database', 'red');
  363. HITStorage.indexedDB.db = e.target.result;
  364. var db = HITStorage.indexedDB.db;
  365. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  366. var store = trans.objectStore("HIT");
  367. var index = store.index('date');
  368. var range = IDBKeyRange.bound(DAYS_TO_FETCH_CHECK[DAYS_TO_FETCH_CHECK.length-1].date, DAYS_TO_FETCH_CHECK[0].date, false, false);
  369.  
  370. index.count(range).onsuccess = function(event) {
  371. var count = event.target.result;
  372. var submitted_hits = 0;
  373.  
  374. for (var i=0; i<DAYS_TO_FETCH_CHECK.length; i++)
  375. {
  376. submitted_hits += DAYS_TO_FETCH_CHECK[i].submitted;
  377. }
  378.  
  379. if (submitted_hits == count)
  380. {
  381. db.close();
  382. HITStorage.update_done();
  383. }
  384. else
  385. {
  386. if (confirm("? ERROR! Number of HITs in DataBase does not match number of HITs available! (" + count + " != " + submitted_hits + ")\n"
  387. + "Would you like to refetch all status pages now?"))
  388. {
  389. db.close();
  390. DAYS_TO_FETCH = DAYS_TO_FETCH_CHECK.slice(0);
  391. HITStorage.update_status_label('Please wait: new script monkeys are fetching relevant status pages', 'red');
  392. setTimeout(function() { HITStorage.do_update(DAYS_TO_FETCH.length); }, 100);
  393. }
  394. else
  395. {
  396. db.close();
  397. HITStorage.update_done();
  398. }
  399. }
  400. };
  401. }
  402. };
  403.  
  404. HITStorage.prepare_update = function()
  405. {
  406. var request = indexedDB.open("HITDB", v);
  407. request.onsuccess = function(e) {
  408. HITStorage.indexedDB.db = e.target.result;
  409. var db = HITStorage.indexedDB.db;
  410. var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_ONLY);
  411. var store = trans.objectStore("STATS");
  412. var range = IDBKeyRange.bound(DAYS_TO_FETCH[DAYS_TO_FETCH.length-1].date, DAYS_TO_FETCH[0].date, false, false);
  413.  
  414. store.openCursor(range).onsuccess = function(event) {
  415. var cursor = event.target.result;
  416. if (cursor && DAYS_TO_FETCH.length > 0)
  417. {
  418. for (var i=0; i<DAYS_TO_FETCH.length; i++)
  419. {
  420. if ( cursor.value.date == DAYS_TO_FETCH[i].date
  421. && cursor.value.submitted == DAYS_TO_FETCH[i].submitted
  422. && cursor.value.approved == DAYS_TO_FETCH[i].approved
  423. && cursor.value.rejected == DAYS_TO_FETCH[i].rejected
  424. && cursor.value.pending == DAYS_TO_FETCH[i].pending)
  425. {
  426. // This day is already in DB and stats match => no need to fetch
  427. // unless there are 'Approved - Pending Payment' HITs
  428. if (DAYS_TO_FETCH[i].pending_payments === undefined || DAYS_TO_FETCH[i].pending_payments == false)
  429. DAYS_TO_FETCH.splice(i,1);
  430. }
  431. }
  432. cursor.continue();
  433. }
  434. else
  435. {
  436. if (DAYS_TO_FETCH.length>0) {
  437. db.close();
  438. setTimeout(function() { HITStorage.do_update(DAYS_TO_FETCH.length); }, 100);
  439. }
  440. else
  441. {
  442. db.close();
  443. HITStorage.update_done();
  444. }
  445. }
  446. };
  447. }
  448. };
  449.  
  450. HITStorage.indexedDB.term_matches_HIT = function(term, hit)
  451. {
  452. var keys = ['date', 'requesterName', 'title', 'feedback', 'hitId', 'requesterId'];
  453. var re = new RegExp(escapeRegExp(term),"ig");
  454. for (var k in keys)
  455. {
  456. //for testing
  457. if (hit[keys[k]] != null && re.test(hit[keys[k]].trim()))
  458. {
  459. return true;
  460. }
  461. }
  462. return false;
  463. }
  464.  
  465. function escapeRegExp(str) {
  466. return str.trim().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  467. }
  468.  
  469. HITStorage.indexedDB.matchHIT = function(hit, options)
  470. {
  471. if (options.status == '---' || hit.status.match(options.status))
  472. {
  473. if (options.search_term == '' || HITStorage.indexedDB.term_matches_HIT(options.term, hit))
  474. {
  475. return true;
  476. }
  477. }
  478. return false;
  479. }
  480.  
  481. function hit_sort_func()
  482. {
  483. return function(a,b) {
  484. if (a.date == b.date) {
  485. if (a.requesterName < b.requesterName)
  486. return -1;
  487. if (a.requesterName > b.requesterName)
  488. return 1;
  489. if (a.title < b.title)
  490. return -1;
  491. if (a.title > b.title)
  492. return 1;
  493. if (a.status < b.status)
  494. return -1;
  495. if (a.status > b.status)
  496. return 1;
  497. }
  498. if (a.date > b.date)
  499. return 1;
  500. if (a.date < b.date)
  501. return -1;
  502. };
  503. }
  504.  
  505. HITStorage.indexedDB.getHITs = function(options) {
  506. var request = indexedDB.open("HITDB", v);
  507. request.onsuccess = function(e) {
  508. HITStorage.indexedDB.db = e.target.result;
  509. var db = HITStorage.indexedDB.db;
  510. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  511. var store = trans.objectStore("HIT");
  512.  
  513. var req;
  514. var results = [];
  515. var index;
  516. var range;
  517.  
  518. if (options.from_date || options.to_date)
  519. {
  520. if (options.from_date != '' || options.to_date != '')
  521. {
  522. index = store.index('date');
  523. if (options.from_date == options.to_date)
  524. {
  525. range = IDBKeyRange.only(options.from_date);
  526. }
  527. else if (options.from_date != '' && options.to_date != '')
  528. {
  529. range = IDBKeyRange.bound(options.from_date, options.to_date, false, false);
  530. }
  531. else if (options.from_date == '' && options.to_date != '')
  532. {
  533. range = IDBKeyRange.upperBound(options.to_date, false);
  534. }
  535. else
  536. {
  537. range = IDBKeyRange.lowerBound(options.from_date, false);
  538. }
  539. req = index.openCursor(range);
  540. }
  541. }
  542. else if (options.index && options.index != '')
  543. {
  544. index = store.index(options.index);
  545. range = IDBKeyRange.only(options.term);
  546. req = index.openCursor(range);
  547. }
  548. else if (options.status == 'Rejected' || options.status == 'Pending Approval'
  549. || options.status == 'Approved' || options.status == 'Paid')
  550. {
  551. var s = (options.status == 'Approved')? 'Approved&nbsp;- Pending&nbsp;Payment' : options.status;
  552. options.index = 'status';
  553. index = store.index(options.index);
  554. range = IDBKeyRange.only(s);
  555. req = index.openCursor(range);
  556. }
  557. else
  558. {
  559. req = store.openCursor();
  560. }
  561.  
  562. req.onsuccess = function(event) {
  563. var cursor = event.target.result;
  564. if (cursor) {
  565. if (HITStorage.indexedDB.matchHIT(cursor.value, options))
  566. results.push(cursor.value);
  567.  
  568. cursor.continue();
  569. }
  570. else {
  571. results.sort(hit_sort_func());
  572.  
  573. if (options.export_csv && options.export_csv == true)
  574. HITStorage.export_csv(results);
  575. else
  576. HITStorage.show_results(results);
  577.  
  578. if (options.donut == '---')
  579. document.getElementById('container').style.display = 'none';
  580. else if (options.donut != '')
  581. HITStorage.prepare_donut(results, options.donut);
  582. }
  583. db.close();
  584. };
  585. };
  586. request.onerror = HITStorage.indexedDB.onerror;
  587. };
  588.  
  589. //
  590. // Show summary of all requesters
  591. //
  592. HITStorage.indexedDB.requesterOverview = function(options) {
  593. var request = indexedDB.open("HITDB", v);
  594. request.onsuccess = function(e) {
  595. HITStorage.indexedDB.db = e.target.result;
  596. var db = HITStorage.indexedDB.db;
  597. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  598. var store = trans.objectStore("HIT");
  599. var index;
  600. var req;
  601.  
  602. // [ requesterId, requesterName, sum(hits), sum(rewards), rejected, pending ]
  603. var results = [];
  604. var tmp_results = {};
  605. if (options.from_date || options.to_date)
  606. {
  607. if (options.from_date != '' || options.to_date != '')
  608. {
  609. index = store.index('date');
  610. if (options.from_date == options.to_date)
  611. {
  612. range = IDBKeyRange.only(options.from_date);
  613. }
  614. else if (options.from_date != '' && options.to_date != '')
  615. {
  616. range = IDBKeyRange.bound(options.from_date, options.to_date, false, false);
  617. }
  618. else if (options.from_date == '' && options.to_date != '')
  619. {
  620. range = IDBKeyRange.upperBound(options.to_date, false);
  621. }
  622. else
  623. {
  624. range = IDBKeyRange.lowerBound(options.from_date, false);
  625. }
  626. req = index.openCursor(range);
  627. }
  628. req.onsuccess = function(event) {
  629. var cursor = event.target.result;
  630. if (cursor) {
  631. var hit = cursor.value;
  632. var rejected = (hit.status == 'Rejected') ? 1 : 0;
  633. var pending = (hit.status.match(/Approved|Paid|Rejected/) == null) ? 1 : 0;
  634. var reward = (pending>0 || rejected>0 )? 0: hit.reward;
  635.  
  636. if (tmp_results[hit.requesterId] === undefined)
  637. {
  638. tmp_results[hit.requesterId] = [];
  639. tmp_results[hit.requesterId][0] = hit.requesterId;
  640. tmp_results[hit.requesterId][1] = hit.requesterName;
  641. tmp_results[hit.requesterId][2] = 1;
  642. tmp_results[hit.requesterId][3] = reward;
  643. tmp_results[hit.requesterId][4] = rejected;
  644. tmp_results[hit.requesterId][5] = pending;
  645. }
  646. else
  647. {
  648. tmp_results[hit.requesterId][1] = hit.requesterName;
  649. tmp_results[hit.requesterId][2] += 1;
  650. tmp_results[hit.requesterId][3] += reward;
  651. tmp_results[hit.requesterId][4] += rejected;
  652. tmp_results[hit.requesterId][5] += pending;
  653. }
  654. cursor.continue();
  655. }
  656. else {
  657. for (var key in tmp_results) {
  658. results.push(tmp_results[key]);
  659. }
  660. // sort by total reward
  661. results.sort(function(a,b) { return b[3]-a[3]; });
  662. if (options.export_csv == true)
  663. HITStorage.show_requester_overview_csv(results);
  664. else
  665. HITStorage.show_requester_overview(results, '(' + options.from_date + '–' + options.to_date + ')');
  666. HITStorage.update_status_label('Script monkeys are ready', 'green');
  667. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  668. HITStorage.enable_inputs();
  669. }
  670. db.close();
  671. };
  672. }
  673. else {
  674. index = store.index('requesterId');
  675. req = index.openCursor();
  676. req.onsuccess = function(event) {
  677. var cursor = event.target.result;
  678. if (cursor) {
  679. var hit = cursor.value;
  680. var rejected = (hit.status == 'Rejected') ? 1 : 0;
  681. var pending = (hit.status.match(/Approved|Paid|Rejected/) == null) ? 1 : 0;
  682. var reward = (pending>0 || rejected>0 )? 0: hit.reward;
  683. if (results.length == 0)
  684. {
  685. results.push([hit.requesterId, hit.requesterName, 1, reward, rejected, pending]);
  686. }
  687. else if (results[0][0] == hit.requesterId)
  688. {
  689. results[0][2] += 1;
  690. results[0][3] += reward;
  691. results[0][4] += rejected;
  692. results[0][5] += pending;
  693. }
  694. else
  695. {
  696. results.unshift([hit.requesterId, hit.requesterName, 1, reward, rejected, pending]);
  697. }
  698. cursor.continue();
  699. }
  700. else {
  701. // sort by total reward
  702. results.sort(function(a,b) { return b[3]-a[3]; });
  703. if (options.export_csv == true)
  704. HITStorage.show_requester_overview_csv(results);
  705. else
  706. HITStorage.show_requester_overview(results);
  707. HITStorage.update_status_label('Script monkeys are ready', 'green');
  708. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  709. HITStorage.enable_inputs();
  710. }
  711. db.close();
  712. };
  713. }
  714. };
  715. request.onerror = HITStorage.indexedDB.onerror;
  716. };
  717.  
  718. //
  719. // Show summary of one requester
  720. //
  721. HITStorage.indexedDB.showRequester = function(requesterId) {
  722. var request = indexedDB.open("HITDB", v);
  723. request.onsuccess = function(e) {
  724. HITStorage.indexedDB.db = e.target.result;
  725. var db = HITStorage.indexedDB.db;
  726. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  727. var store = trans.objectStore("HIT");
  728. var index;
  729. var results = [];
  730.  
  731. index = store.index('requesterId');
  732. var range = IDBKeyRange.only(requesterId);
  733. index.openCursor(range).onsuccess = function(event) {
  734. var cursor = event.target.result;
  735. if (cursor) {
  736. results.push(cursor.value);
  737. cursor.continue();
  738. }
  739. else {
  740. results.sort(function(a,b)
  741. {
  742. if (a.date > b.date)
  743. return -1;
  744. if (a.date < b.date)
  745. return 1;
  746. return 0;
  747. });
  748. HITStorage.show_requester(results);
  749. HITStorage.update_status_label('Script monkeys are ready', 'green');
  750. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  751. HITStorage.enable_inputs();
  752. }
  753. db.close();
  754. };
  755. };
  756. request.onerror = HITStorage.indexedDB.onerror;
  757. };
  758.  
  759.  
  760. // Show summary of pending HITs
  761. HITStorage.indexedDB.pendingOverview = function(options) {
  762. var request = indexedDB.open("HITDB", v);
  763. request.onsuccess = function(e) {
  764. HITStorage.indexedDB.db = e.target.result;
  765. var db = HITStorage.indexedDB.db;
  766. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  767. var store = trans.objectStore("HIT");
  768. var index;
  769. var req;
  770.  
  771. // [ requesterId, requesterName, sum(pendings), sum(rewards) ]
  772. var results = [];
  773. var tmp_results = {};
  774.  
  775. index = store.index('status');
  776. range = IDBKeyRange.only('Pending Approval');
  777. index.openCursor(range).onsuccess = function(event) {
  778. var cursor = event.target.result;
  779. if (cursor) {
  780. var hit = cursor.value;
  781. console.log(hit);
  782. if (tmp_results[hit.requesterId] === undefined)
  783. {
  784. tmp_results[hit.requesterId] = [];
  785. tmp_results[hit.requesterId][0] = hit.requesterId;
  786. tmp_results[hit.requesterId][1] = hit.requesterName;
  787. tmp_results[hit.requesterId][2] = 1;
  788. tmp_results[hit.requesterId][3] = hit.reward;
  789. }
  790. else
  791. {
  792. tmp_results[hit.requesterId][1] = hit.requesterName;
  793. tmp_results[hit.requesterId][2] += 1;
  794. tmp_results[hit.requesterId][3] += hit.reward;
  795. }
  796. cursor.continue();
  797. }
  798. else {
  799. for (var key in tmp_results) {
  800. results.push(tmp_results[key]);
  801. }
  802. // sort by pending hits
  803. results.sort(function(a,b) { return b[2]-a[2]; });
  804. if (options.export_csv == true)
  805. HITStorage.show_pending_overview_csv(results);
  806. else
  807. HITStorage.show_pending_overview(results);
  808. HITStorage.update_status_label('Script monkeys are ready', 'green');
  809. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  810. HITStorage.enable_inputs();
  811. }
  812. db.close();
  813. };
  814. };
  815. request.onerror = HITStorage.indexedDB.onerror;
  816. };
  817.  
  818. // Show summary of daily stats
  819. HITStorage.indexedDB.statusOverview = function(options) {
  820. var request = indexedDB.open("HITDB", v);
  821. request.onsuccess = function(e) {
  822. HITStorage.indexedDB.db = e.target.result;
  823. var db = HITStorage.indexedDB.db;
  824. var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_ONLY);
  825. var store = trans.objectStore("STATS");
  826. var req;
  827.  
  828. var results = [];
  829.  
  830. if (options.from_date || options.to_date)
  831. {
  832. if (options.from_date != '' || options.to_date != '')
  833. {
  834. if (options.from_date == options.to_date)
  835. {
  836. range = IDBKeyRange.only(options.from_date);
  837. }
  838. else if (options.from_date != '' && options.to_date != '')
  839. {
  840. range = IDBKeyRange.bound(options.from_date, options.to_date, false, false);
  841. }
  842. else if (options.from_date == '' && options.to_date != '')
  843. {
  844. range = IDBKeyRange.upperBound(options.to_date, false);
  845. }
  846. else
  847. {
  848. range = IDBKeyRange.lowerBound(options.from_date, false);
  849. }
  850. req = store.openCursor(range);
  851. }
  852. }
  853. else
  854. {
  855. req = store.openCursor();
  856. }
  857. req.onsuccess = function(event) {
  858. var cursor = event.target.result;
  859. if (cursor) {
  860. if (cursor.value.submitted > 0)
  861. results.push(cursor.value);
  862. cursor.continue();
  863. }
  864. else {
  865. if (options.export_csv == true)
  866. HITStorage.show_status_overview_csv(results);
  867. else
  868. HITStorage.show_status_overview(results, '(' + options.from_date + '–' + options.to_date + ')');
  869. HITStorage.update_status_label('Script monkeys are ready', 'green');
  870. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  871. HITStorage.enable_inputs();
  872. }
  873. db.close();
  874. };
  875. };
  876. request.onerror = HITStorage.indexedDB.onerror;
  877. };
  878.  
  879. HITStorage.indexedDB.getHIT = function(id) {
  880. var request = indexedDB.open("HITDB", v);
  881. request.onsuccess = function(e) {
  882. HITStorage.indexedDB.db = e.target.result;
  883. var db = HITStorage.indexedDB.db;
  884. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  885. var store = trans.objectStore("HIT");
  886.  
  887. var request = store.get(id);
  888.  
  889. request.onsuccess = function(e) {
  890. db.close();
  891. showDetails(e.target.result.note);
  892. };
  893.  
  894. request.onerror = function(e) {
  895. console.log("Error Getting: ", e);
  896. };
  897. };
  898. request.onerror = HITStorage.indexedDB.onerror;
  899. };
  900.  
  901. HITStorage.indexedDB.addNote = function(id, note) {
  902. var request = indexedDB.open("HITDB", v);
  903. request.onsuccess = function(e) {
  904. HITStorage.indexedDB.db = e.target.result;
  905. var db = HITStorage.indexedDB.db;
  906. var trans = db.transaction(["NOTES"], HITStorage.IDBTransactionModes.READ_WRITE);
  907. var store = trans.objectStore("NOTES");
  908. var request;
  909.  
  910. if (note == '')
  911. request = store.delete(id);
  912. else
  913. request = store.put({requesterId: id, note: note});
  914.  
  915. request.onsuccess = function(e) {
  916. db.close();
  917. };
  918.  
  919. request.onerror = function(e) {
  920. console.log("Error Adding: ", e);
  921. };
  922. };
  923. request.onerror = HITStorage.indexedDB.onerror;
  924. };
  925.  
  926. HITStorage.indexedDB.blockHITS = function(requesterId, title, hitElement, titleElement) {
  927. var request = indexedDB.open("HITDB", v);
  928. request.onsuccess = function(e) {
  929. HITStorage.indexedDB.db = e.target.result;
  930. var db = HITStorage.indexedDB.db;
  931.  
  932. if (!db.objectStoreNames.contains("BLOCKS"))
  933. {
  934. db.close();
  935. return;
  936. }
  937. var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_ONLY);
  938. var store = trans.objectStore("BLOCKS");
  939. var index = store.index("requesterId");
  940. var range = IDBKeyRange.only(requesterId);
  941.  
  942. index.openCursor(range).onsuccess = function(event) {
  943. var cursor = event.target.result;
  944. if (cursor && cursor.value.re)
  945. {
  946. if (cursor.value.re.test(title))
  947. {
  948. hitElement.style.display = 'none';
  949. titleElement.addEventListener("click", unblock_func(requesterId, title));
  950.  
  951. titleElement.style.fontSize = 'small';
  952.  
  953. // move blocked hits to the bottom
  954. var table = hitElement.parentNode.parentNode.parentNode.parentNode.parentNode;
  955. var hit = hitElement.parentNode.parentNode.parentNode.parentNode;
  956. table.removeChild(hit);
  957. table.appendChild(hit);
  958. }
  959. cursor.continue();
  960. }
  961. else
  962. {
  963. db.close();
  964. }
  965. };
  966. };
  967. request.onerror = HITStorage.indexedDB.onerror;
  968. };
  969.  
  970. HITStorage.indexedDB.addBlock = function(requesterId, re) {
  971. var request = indexedDB.open("HITDB", v);
  972. request.onsuccess = function(e) {
  973. HITStorage.indexedDB.db = e.target.result;
  974. var db = HITStorage.indexedDB.db;
  975. var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_WRITE);
  976. var store = trans.objectStore("BLOCKS");
  977. var request;
  978.  
  979. request = store.put({requesterId: requesterId, re: re});
  980.  
  981. request.onsuccess = function(e) {
  982. db.close();
  983. };
  984. };
  985. request.onerror = HITStorage.indexedDB.onerror;
  986. };
  987.  
  988. // Removes all blocks for requesterId, where RE matches this HIT title
  989. HITStorage.indexedDB.removeBlocks = function(requesterId, title) {
  990. var request = indexedDB.open("HITDB", v);
  991. request.onsuccess = function(e) {
  992. HITStorage.indexedDB.db = e.target.result;
  993. var db = HITStorage.indexedDB.db;
  994. if (!db.objectStoreNames.contains("BLOCKS"))
  995. {
  996. db.close();
  997. return;
  998. }
  999. var trans = db.transaction(["BLOCKS"], HITStorage.IDBTransactionModes.READ_WRITE);
  1000. var store = trans.objectStore("BLOCKS");
  1001. var index = store.index("requesterId");
  1002. var range = IDBKeyRange.only(requesterId);
  1003.  
  1004. index.openCursor(range).onsuccess = function(event)
  1005. {
  1006. var cursor = event.target.result;
  1007. if (cursor)
  1008. {
  1009. if (cursor.value.re.test(title))
  1010. store.delete(cursor.value.id);
  1011. db.close();
  1012. }
  1013. };
  1014. };
  1015. request.onerror = HITStorage.indexedDB.onerror;
  1016. };
  1017.  
  1018. HITStorage.indexedDB.updateNoteButton = function(id, label) {
  1019. var request = indexedDB.open("HITDB", v);
  1020. request.onsuccess = function(e) {
  1021. HITStorage.indexedDB.db = e.target.result;
  1022. var db = HITStorage.indexedDB.db;
  1023.  
  1024. if (!db.objectStoreNames.contains("NOTES"))
  1025. {
  1026. label.title = 'Update HIT database on statusdetail page to use this feature';
  1027. db.close();
  1028. return;
  1029. }
  1030. var trans = db.transaction(["NOTES"], HITStorage.IDBTransactionModes.READ_ONLY);
  1031. var store = trans.objectStore("NOTES");
  1032.  
  1033. store.get(id).onsuccess = function(event)
  1034. {
  1035. if (event.target.result === undefined)
  1036. {
  1037. label.textContent = '';
  1038. }
  1039. else
  1040. {
  1041. var note = event.target.result.note;
  1042. label.textContent = note;
  1043. label.style.border = '1px dotted';
  1044. if (note.indexOf('!') >= 0)
  1045. label.style.color = 'red';
  1046. else
  1047. label.style.color = 'black';
  1048. }
  1049. db.close();
  1050. };
  1051. };
  1052. request.onerror = HITStorage.indexedDB.onerror;
  1053. };
  1054.  
  1055.  
  1056. HITStorage.indexedDB.colorRequesterButton = function(id, button) {
  1057. var request = indexedDB.open("HITDB", v);
  1058. request.onsuccess = function(e) {
  1059. HITStorage.indexedDB.db = e.target.result;
  1060. var db = HITStorage.indexedDB.db;
  1061. if (!db.objectStoreNames.contains("HIT"))
  1062. {
  1063. button.title = 'Update HIT database on statusdetail page to use this feature';
  1064. db.close();
  1065. return;
  1066. }
  1067. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1068. var store = trans.objectStore("HIT");
  1069.  
  1070. var index = store.index("requesterId");
  1071. index.get(id).onsuccess = function(event)
  1072. {
  1073. if (event.target.result === undefined)
  1074. {
  1075. button.style.backgroundColor = 'pink';
  1076. }
  1077. else
  1078. {
  1079. button.style.backgroundColor = 'lightgreen';
  1080. button.style.fontWeight = 'bold';
  1081. }
  1082. db.close();
  1083. };
  1084. };
  1085. request.onerror = HITStorage.indexedDB.onerror;
  1086. };
  1087.  
  1088. HITStorage.indexedDB.colorTitleButton = function(title, button) {
  1089. var request = indexedDB.open("HITDB", v);
  1090. request.onsuccess = function(e) {
  1091. HITStorage.indexedDB.db = e.target.result;
  1092. var db = HITStorage.indexedDB.db;
  1093. if (!db.objectStoreNames.contains("HIT"))
  1094. {
  1095. button.title = 'Update HIT database on statusdetail page to use this feature';
  1096. db.close();
  1097. return;
  1098. }
  1099. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1100. var store = trans.objectStore("HIT");
  1101.  
  1102. var index = store.index("title");
  1103. index.get(title).onsuccess = function(event)
  1104. {
  1105. if (event.target.result === undefined)
  1106. {
  1107. button.style.backgroundColor = 'pink';
  1108. }
  1109. else
  1110. {
  1111. button.style.backgroundColor = 'lightgreen';
  1112. button.style.fontWeight = 'bold';
  1113. }
  1114.  
  1115. db.close();
  1116. };
  1117. };
  1118. request.onerror = HITStorage.indexedDB.onerror;
  1119. };
  1120.  
  1121. HITStorage.indexedDB.deleteDB = function () {
  1122. var deleteRequest = indexedDB.deleteDatabase("HITDB");
  1123. deleteRequest.onsuccess = function (e)
  1124. {
  1125. alert("deleted");
  1126. }
  1127. deleteRequest.onblocked = function (e)
  1128. {
  1129. alert("blocked");
  1130. }
  1131. deleteRequest.onerror = HITStorage.indexedDB.onerror;
  1132. }
  1133.  
  1134. HITStorage.indexedDB.get_pending_approvals = function() {
  1135. var element = document.getElementById('pending_earnings_value');
  1136. var header_element = document.getElementById('pending_earnings_header');
  1137. if (element == null)
  1138. return;
  1139.  
  1140. var request = indexedDB.open("HITDB", v);
  1141. request.onsuccess = function(e) {
  1142. HITStorage.indexedDB.db = e.target.result;
  1143. var db = HITStorage.indexedDB.db;
  1144. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1145. var store = trans.objectStore("HIT");
  1146.  
  1147. var result = 0;
  1148. var index;
  1149. var range;
  1150.  
  1151. index = store.index('status');
  1152. range = IDBKeyRange.only('Pending Approval');
  1153.  
  1154. index.openCursor(range).onsuccess = function(event) {
  1155. var cursor = event.target.result;
  1156. if (cursor) {
  1157. result += cursor.value.reward;
  1158. cursor.continue();
  1159. }
  1160. else {
  1161. element.textContent = '$' + result.toFixed(2);
  1162. if (header_element != null)
  1163. header_element.textContent = 'Pending earnings (HITDB updated: ' + localStorage['HITDB UPDATED']+ ')';
  1164. }
  1165. db.close();
  1166. };
  1167. };
  1168. request.onerror = HITStorage.indexedDB.onerror;
  1169. };
  1170.  
  1171. HITStorage.indexedDB.get_pending_payments = function() {
  1172. var element = document.getElementById('pending_earnings_value');
  1173. if (element == null)
  1174. return;
  1175.  
  1176. var request = indexedDB.open("HITDB", v);
  1177. request.onsuccess = function(e) {
  1178. HITStorage.indexedDB.db = e.target.result;
  1179. var db = HITStorage.indexedDB.db;
  1180. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1181. var store = trans.objectStore("HIT");
  1182.  
  1183. var result = 0;
  1184. var index;
  1185. var range;
  1186.  
  1187. index = store.index('status');
  1188. range = IDBKeyRange.only('Approved&nbsp;- Pending&nbsp;Payment');
  1189.  
  1190. index.openCursor(range).onsuccess = function(event) {
  1191. var cursor = event.target.result;
  1192. if (cursor) {
  1193. result += cursor.value.reward;
  1194. cursor.continue();
  1195. }
  1196. else {
  1197. element.title = 'Approved - Pending Payment: $' + result.toFixed(2);
  1198. }
  1199. }
  1200. db.close();
  1201. };
  1202. request.onerror = HITStorage.indexedDB.onerror;
  1203. };
  1204.  
  1205. HITStorage.indexedDB.get_todays_projected_earnings = function(date) {
  1206. var element = document.getElementById('projected_earnings_value');
  1207. if (element == null)
  1208. return;
  1209.  
  1210. var request = indexedDB.open("HITDB", v);
  1211. request.onsuccess = function(e) {
  1212. HITStorage.indexedDB.db = e.target.result;
  1213. var db = HITStorage.indexedDB.db;
  1214. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_ONLY);
  1215. var store = trans.objectStore("HIT");
  1216.  
  1217. var result = 0;
  1218. var rejected = 0;
  1219. var index;
  1220. var range;
  1221.  
  1222. index = store.index('date');
  1223. range = IDBKeyRange.only(date);
  1224.  
  1225. index.openCursor(range).onsuccess = function(event) {
  1226. var cursor = event.target.result;
  1227. if (cursor) {
  1228. if (cursor.value.status == 'Rejected')
  1229. rejected += cursor.value.reward;
  1230. else
  1231. result += cursor.value.reward;
  1232. cursor.continue();
  1233. }
  1234. else {
  1235. element.textContent = '$' + result.toFixed(2);
  1236. element.title = '$' + rejected.toFixed(2) + ' rejected';
  1237.  
  1238. if (localStorage['TODAYS TARGET'] !== undefined)
  1239. {
  1240. var target = parseFloat(localStorage['TODAYS TARGET']).toFixed(2);
  1241. var my_target = document.getElementById('my_target');
  1242.  
  1243. var progress = Math.floor(result/target*40);
  1244. if (progress > 40)
  1245. progress = 40;
  1246. my_target.innerHTML = progress_bar(progress, 40, '█', '█', '#7fb448', 'grey') + '&nbsp;' +
  1247. ((result>target)? '+' : '') + (result-target).toFixed(2);
  1248. my_target.style.fontSize = '9px';
  1249. }
  1250. }
  1251. }
  1252. db.close();
  1253. };
  1254. request.onerror = HITStorage.indexedDB.onerror;
  1255. };
  1256.  
  1257. // Update database date format from MMDDYYYY to YYYY-MM-DD
  1258. // Shouldn't break anything even if used on already updated db
  1259. HITStorage.update_date_format = function(verbose)
  1260. {
  1261. var request = indexedDB.open("HITDB", v);
  1262. request.onsuccess = function(e) {
  1263. HITStorage.indexedDB.db = e.target.result;
  1264. var db = HITStorage.indexedDB.db;
  1265. var trans = db.transaction(["HIT"], HITStorage.IDBTransactionModes.READ_WRITE);
  1266. var store = trans.objectStore("HIT");
  1267.  
  1268. store.openCursor().onsuccess = function(event) {
  1269. var cursor = event.target.result;
  1270. if (cursor)
  1271. {
  1272. if (cursor.value.date.indexOf('-') < 0)
  1273. {
  1274. var i = cursor.value;
  1275. i.date = convert_date(i.date);
  1276. i.requesterName = i.requesterName.trim();
  1277. i.title = i.title.trim();
  1278. cursor.update(i);
  1279. }
  1280. cursor.continue();
  1281. }
  1282. else
  1283. {
  1284. db.close();
  1285. HITStorage.update_stats_date_format(verbose);
  1286. }
  1287. };
  1288. }
  1289. }
  1290.  
  1291. HITStorage.update_stats_date_format = function(verbose)
  1292. {
  1293. var request = indexedDB.open("HITDB", v);
  1294. request.onsuccess = function(e) {
  1295. HITStorage.indexedDB.db = e.target.result;
  1296. var db = HITStorage.indexedDB.db;
  1297. var trans = db.transaction(["STATS"], HITStorage.IDBTransactionModes.READ_WRITE);
  1298. var store = trans.objectStore("STATS");
  1299.  
  1300. store.openCursor().onsuccess = function(event) {
  1301. var cursor = event.target.result;
  1302. if (cursor)
  1303. {
  1304. if (cursor.value.date.indexOf('-') < 0)
  1305. {
  1306. var i = cursor.value;
  1307. i.date = convert_date(i.date);
  1308. cursor.delete();
  1309. store.put(i);
  1310. }
  1311. cursor.continue();
  1312. }
  1313. else
  1314. {
  1315. // DB should be fully updated
  1316. db.close();
  1317. if (verbose == true)
  1318. alert('Date conversion done.');
  1319. }
  1320. };
  1321. }
  1322. };
  1323.  
  1324. /* ------------------------------------------------------------- */
  1325.  
  1326. HITStorage.prepare_donut = function (donutData, type)
  1327. {
  1328. if (type == '---')
  1329. return;
  1330. var countHits = true;
  1331. if (type.match('REWARDS'))
  1332. countHits = false;
  1333.  
  1334. var tmpData = {};
  1335. var topRequesters = [];
  1336. var topHits = [];
  1337. var sum = 0;
  1338.  
  1339. for (var i=0; i < donutData.length; i++) {
  1340. var requesterName = donutData[i].requesterName.trim() + " (" + donutData[i].requesterId + ")";
  1341. var hitTitle = donutData[i].title;
  1342. var hitReward = donutData[i].reward;
  1343. sum += (countHits) ? 1 : hitReward;
  1344.  
  1345. if (tmpData[requesterName]) {
  1346. tmpData[requesterName]['HITS'] += (countHits) ? 1 : hitReward;
  1347. }
  1348. else {
  1349. tmpData[requesterName] = {};
  1350. tmpData[requesterName]['HITS'] = (countHits) ? 1 : hitReward;
  1351. }
  1352. if (tmpData[requesterName][hitTitle])
  1353. tmpData[requesterName][hitTitle] += (countHits) ? 1 : hitReward;
  1354. else
  1355. tmpData[requesterName][hitTitle] = (countHits) ? 1 : hitReward;
  1356.  
  1357. }
  1358.  
  1359. for (var key in tmpData) {
  1360. topRequesters.push({name: key, y: tmpData[key]['HITS']});
  1361. }
  1362. topRequesters.sort(function(a,b){return b.y-a.y});
  1363.  
  1364. var colors = Highcharts.getOptions().colors;
  1365.  
  1366. for (var i=0; i<topRequesters.length; i++) {
  1367. var tmpHits = [];
  1368. topRequesters[i].color = colors[i];
  1369. for (var key2 in tmpData[topRequesters[i].name]) {
  1370. if (key2 != 'HITS') {
  1371. tmpHits.push({name: key2, y: tmpData[topRequesters[i].name][key2], color: colors[i]});
  1372. }
  1373. }
  1374. tmpHits.sort(function(a,b){return b.y-a.y});
  1375. for (var j=0; j<tmpHits.length ; j++) {
  1376. var brightness = 0.2 - (j / tmpHits.length) / 5;
  1377. tmpHits[j].color = Highcharts.Color(colors[i]).brighten(brightness).get();
  1378. }
  1379. topHits = topHits.concat(tmpHits);
  1380. }
  1381.  
  1382. document.getElementById('container').style.display = 'block';
  1383.  
  1384.  
  1385. chart = new Highcharts.Chart({
  1386. chart: {
  1387. renderTo: 'container',
  1388. type: 'pie'
  1389. },
  1390. title: {
  1391. text: 'Requesters and HITs matching your latest search'
  1392. },
  1393. yAxis: {
  1394. title: {
  1395. text: ''
  1396. }
  1397. },
  1398. plotOptions: {
  1399. pie: {
  1400. shadow: false,
  1401. dataLabels: { enabled: true}
  1402. }
  1403. },
  1404. tooltip: {
  1405. animation: false,
  1406. valuePrefix: (countHits)? '' : '$',
  1407. valueSuffix: (countHits)? ' HITs' : '',
  1408. valueDecimals: (countHits)? 0 : 2,
  1409. pointFormat: (countHits)? '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> (of all ' + sum + ' HITs)<br/>' :
  1410. '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> (of all $' + sum.toFixed(2) + ')<br/>'
  1411. },
  1412. series: [{
  1413. name: 'Requesters',
  1414. data: topRequesters,
  1415. size: '60%',
  1416. dataLabels: {
  1417. formatter: function() {
  1418. if (countHits) {
  1419. return this.y/sum >= 0.20 ? this.point.name: null;
  1420. }
  1421. else {
  1422. return this.y/sum >= 0.20 ? this.point.name : null;
  1423. }
  1424. },
  1425. color: 'black',
  1426. distance: -10
  1427. }
  1428. }, {
  1429. name: 'HITs',
  1430. data: topHits,
  1431. innerSize: '60%',
  1432. dataLabels: {
  1433. formatter: function() {
  1434. if (countHits) {
  1435. return this.y/sum > 0.05 ? this.point.name : null;
  1436. }
  1437. else {
  1438. return this.y/sum > 0.05 ? this.point.name : null;
  1439. }
  1440. },
  1441. color: 'black',
  1442. }
  1443. }]
  1444. });
  1445. }
  1446.  
  1447. // Stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331)
  1448. HITStorage.getHTTPObject = function()
  1449. {
  1450. if (typeof XMLHttpRequest != 'undefined')
  1451. {
  1452. return new XMLHttpRequest();
  1453. }
  1454. try
  1455. {
  1456. return new ActiveXObject("Msxml2.XMLHTTP");
  1457. }
  1458. catch (e)
  1459. {
  1460. try
  1461. {
  1462. return new ActiveXObject("Microsoft.XMLHTTP");
  1463. }
  1464. catch (e) {}
  1465. }
  1466. return false;
  1467. }
  1468.  
  1469. // Stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331)
  1470. // date format MMDDYYYY!
  1471. HITStorage.process_page = function(link, date, hitData)
  1472. {
  1473. var page = HITStorage.getHTTPObject();
  1474. page.open("GET", link, false);
  1475. page.send(null);
  1476. return HITStorage.parse_data(page.responseText, date, hitData);
  1477. }
  1478.  
  1479. // Partly stolen from Today's Projected Earnings (http://userscripts.org/scripts/show/95331)
  1480. // date format MMDDYYYY!
  1481. HITStorage.parse_data = function(page_text, date, hitData)
  1482. {
  1483. var index = 0;
  1484. var index2 = 0;
  1485. var page_html = document.createElement('div');
  1486. page_html.innerHTML = page_text;
  1487.  
  1488. var requesters = page_html.getElementsByClassName('statusdetailRequesterColumnValue');
  1489. var titles = page_html.getElementsByClassName('statusdetailTitleColumnValue');
  1490. var amounts = page_html.getElementsByClassName('statusdetailAmountColumnValue');
  1491. var statuses = page_html.getElementsByClassName('statusdetailStatusColumnValue');
  1492. var feedbacks = page_html.getElementsByClassName('statusdetailRequesterFeedbackColumnValue');
  1493.  
  1494. var requesterName;
  1495. var hitTitle;
  1496. var hitReward;
  1497. var hitStatus;
  1498. var requesterId;
  1499. var hitId;
  1500.  
  1501. for(var k = 0; k < amounts.length; k++)
  1502. {
  1503. requesterName = requesters[k].textContent;
  1504. requesterLink = requesters[k].childNodes[1].href;
  1505. hitTitle = titles[k].textContent;
  1506. index = amounts[k].innerHTML.indexOf('$');
  1507. hitReward = parseFloat(amounts[k].innerHTML.substring(index+1));
  1508. hitStatus = statuses[k].innerHTML;
  1509. hitFeedback = feedbacks[k].textContent;
  1510.  
  1511.  
  1512. requesterId = getQueryVariable(requesterLink,"requesterId");
  1513. subject = getQueryVariable(requesterLink,"subject");
  1514. subject = subject.split("+");
  1515. hitId = subject[subject.length-1];
  1516.  
  1517. var hit = {
  1518. hitId : hitId,
  1519. date : convert_date(date),
  1520. requesterName : requesterName.trim(),
  1521. requesterLink : requesterLink.trim(),
  1522. title : hitTitle.trim(),
  1523. reward : hitReward,
  1524. status : hitStatus,
  1525. feedback : hitFeedback.trim(),
  1526. requesterId : requesterId
  1527. };
  1528.  
  1529. //HITStorage.indexedDB.addHIT(hitData);
  1530. hitData.push(hit);
  1531. }
  1532.  
  1533. return amounts.length;
  1534. }
  1535.  
  1536. //Used to simplify getting requester ID's and such
  1537. function getQueryVariable(url,variable)
  1538. {
  1539. var query = url.substring(1);
  1540. var vars = query.split("?")[1].split("&");
  1541. for (var i=0;i<vars.length;i++)
  1542. {
  1543. var pair = vars[i].split("=");
  1544. if(pair[0] == variable)
  1545. {
  1546. return pair[1];
  1547. }
  1548. }
  1549. return(false);
  1550. }
  1551.  
  1552. // Returns available days (YYYY-MM-DD)
  1553. HITStorage.getAllAvailableDays = function(try_extra_days)
  1554. {
  1555. var days = [];
  1556.  
  1557. var page = HITStorage.getHTTPObject();
  1558. page.open("GET", 'https://www.mturk.com/mturk/status', false);
  1559. page.send(null);
  1560.  
  1561. var page_html = document.createElement('div');
  1562. page_html.innerHTML = page.responseText;
  1563.  
  1564. var dateElements = page_html.getElementsByClassName('statusDateColumnValue');
  1565. var submittedElements = page_html.getElementsByClassName('statusSubmittedColumnValue');
  1566. var approvedElements = page_html.getElementsByClassName('statusApprovedColumnValue');
  1567. var rejectedElements = page_html.getElementsByClassName('statusRejectedColumnValue');
  1568. var pendingElements = page_html.getElementsByClassName('statusPendingColumnValue');
  1569. var earningsElements = page_html.getElementsByClassName('statusEarningsColumnValue');
  1570.  
  1571. for (var i=0; i<dateElements.length; i++)
  1572. {
  1573. var date = dateElements[i].childNodes[1].href.substr(53);
  1574. date = convert_date(date);
  1575.  
  1576. days.push( { date: date,
  1577. submitted: parseInt(submittedElements[i].textContent),
  1578. approved : parseInt(approvedElements[i].textContent),
  1579. rejected : parseInt(rejectedElements[i].textContent),
  1580. pending : parseInt(pendingElements[i].textContent),
  1581. earnings : parseFloat(earningsElements[i].textContent.slice(1)) });
  1582. }
  1583.  
  1584. if (try_extra_days > 0)
  1585. {
  1586. var date = days[days.length-1].date;
  1587. var d = new Date();
  1588. d.setFullYear(parseInt(date.substr(0,4)), parseInt(date.substr(5,2))-1, parseInt(date.substr(8,2)));
  1589.  
  1590. for (var i=0; i<try_extra_days; i++)
  1591. {
  1592. d.setDate(d.getDate()-1);
  1593. var month = '0' + (d.getMonth() + 1);
  1594. var day = '0' + d.getDate();
  1595. if (month.length > 2)
  1596. month = month.substr(1);
  1597. if (day.length > 2)
  1598. day = day.substr(1);
  1599. date = '' + d.getFullYear() + '-' + month + '-' + day;
  1600.  
  1601. days.push( { date: date,
  1602. submitted: -1,
  1603. approved : -1,
  1604. rejected : -1,
  1605. pending : -1,
  1606. earnings : -1 } );
  1607. }
  1608. }
  1609.  
  1610. return days;
  1611. }
  1612.  
  1613. HITStorage.getLatestHITs = function()
  1614. {
  1615. if (localStorage['HITDB AUTO UPDATE'] === undefined || localStorage['HITDB AUTO UPDATE'] == 'OFF')
  1616. return;
  1617.  
  1618. if (localStorage['HITDB TIMESTAMP'] !== undefined)
  1619. {
  1620. if (new Date().getTime() < new Date(parseInt(localStorage['HITDB TIMESTAMP'])).getTime() + 90000)
  1621. {
  1622. return;
  1623. }
  1624. }
  1625. localStorage['HITDB TIMESTAMP'] = new Date().getTime();
  1626.  
  1627. var auto_button = document.getElementById('auto_button');
  1628. var page = HITStorage.getHTTPObject();
  1629. page.open("GET", 'https://www.mturk.com/mturk/status', false);
  1630. page.send(null);
  1631. auto_button.textContent += ' +';
  1632.  
  1633. var page_html = document.createElement('div');
  1634. page_html.innerHTML = page.responseText;
  1635.  
  1636. var dateElements = page_html.getElementsByClassName('statusDateColumnValue');
  1637. var submittedElements = page_html.getElementsByClassName('statusSubmittedColumnValue');
  1638. var approvedElements = page_html.getElementsByClassName('statusApprovedColumnValue');
  1639. var rejectedElements = page_html.getElementsByClassName('statusRejectedColumnValue');
  1640. var pendingElements = page_html.getElementsByClassName('statusPendingColumnValue');
  1641. var earningsElements = page_html.getElementsByClassName('statusEarningsColumnValue');
  1642.  
  1643. if (dateElements[0].childNodes[1].textContent.trim() != 'Today')
  1644. return;
  1645.  
  1646. var url = dateElements[0].childNodes[1].href;
  1647. var date = url.substr(53); // keep MMDDYYYY
  1648. var submitted = parseInt(submittedElements[0].textContent);
  1649. //var approved = parseInt(approvedElements[0].textContent);
  1650. //var rejected = parseInt(rejectedElements[0].textContent);
  1651. //var pending = parseInt(pendingElements[0].textContent);
  1652. //var earnings = parseFloat(earningsElements[0].textContent.slice(1));
  1653. var pages_done = null;
  1654. if (localStorage['HITDB AUTOUPDATE PAGES'] !== undefined)
  1655. {
  1656. pages_done = JSON.parse(localStorage['HITDB AUTOUPDATE PAGES']);
  1657. }
  1658. if (pages_done == null || pages_done.date != date)
  1659. pages_done = {date: date};
  1660.  
  1661. var new_hits = 0;
  1662. var page = 1 + Math.floor(submitted/25);
  1663. page = (page<1) ? 1 : page;
  1664.  
  1665. var hitData = [];
  1666. if (submitted != pages_done.submitted)
  1667. {
  1668. url = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + page + "&encodedDate=" + date;
  1669. HITStorage.process_page(url, date, hitData);
  1670. new_hits += submitted - pages_done.submitted;
  1671. pages_done.submitted = submitted;
  1672. localStorage['HITDB AUTOUPDATE PAGES'] = JSON.stringify(pages_done);
  1673. auto_button.textContent += '+';
  1674. }
  1675.  
  1676. if (page > 1)
  1677. {
  1678. extra_page = page-1;
  1679.  
  1680. while (extra_page >= 1)
  1681. {
  1682. if (pages_done[extra_page] != true)
  1683. {
  1684. url = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + extra_page + "&encodedDate=" + date;
  1685. if (HITStorage.process_page(url, date, hitData) == 25)
  1686. {
  1687. pages_done[extra_page] = true;
  1688. localStorage['HITDB AUTOUPDATE PAGES'] = JSON.stringify(pages_done);
  1689. auto_button.textContent += '+';
  1690. }
  1691. break;
  1692. }
  1693. extra_page -= 1;
  1694. }
  1695. }
  1696. HITStorage.indexedDB.addHITs(hitData);
  1697. }
  1698.  
  1699. // Gets status details for given date (MMDDYYYY)
  1700. // Collects all HITs for given date to hitData array
  1701. HITStorage.getHITData = function(day_to_fetch, hitData, page, days_to_update)
  1702. {
  1703. var dataDate = convert_iso_date(day_to_fetch.date);
  1704. page = page || 1;
  1705. detailed_status_page_link = "https://www.mturk.com/mturk/statusdetail?sortType=All&pageNumber=" + page + "&encodedDate=" + dataDate;
  1706.  
  1707. if (HITStorage.process_page(detailed_status_page_link, dataDate, hitData) == 0)
  1708. {
  1709. if (day_to_fetch.submitted == -1 || hitData.length == day_to_fetch.submitted)
  1710. {
  1711. setTimeout(function(){ HITStorage.indexedDB.addHITs(hitData, day_to_fetch, days_to_update); }, 1000);
  1712. }
  1713. else
  1714. {
  1715. alert("There was an error while fetching HITs for date: " + day_to_fetch.date + ".\n" +
  1716. "Script monkeys expected " + day_to_fetch.submitted + " bananas, but got " + hitData.length + "! ?");
  1717. HITStorage.update_done();
  1718. }
  1719. }
  1720. else
  1721. {
  1722. HITStorage.update_status_label('Please wait: script monkeys are fetching status pages (' +
  1723. day_to_fetch.date + ', page ' + page + ')', 'red');
  1724. setTimeout(function(){ HITStorage.getHITData(day_to_fetch, hitData, page+1, days_to_update); }, 1000);
  1725. }
  1726. }
  1727.  
  1728. HITStorage.formatTime = function(msec)
  1729. {
  1730. if (isNaN(msec))
  1731. return "-";
  1732. var seconds = Math.floor(msec / 1000) % 60;
  1733. var minutes = Math.floor((msec / 1000) / 60) % 60;
  1734. var hours = Math.floor(((msec / 1000) / 60) / 60) % 24;
  1735. var days = Math.floor(((msec / 1000) / 60) / 60 / 24);
  1736.  
  1737. if (hours > 0)
  1738. seconds = "";
  1739. else
  1740. seconds = "" + seconds + "s";
  1741. minutes == 0 ? minutes = "" : minutes = "" + minutes + "m ";
  1742. hours == 0 ? hours = "" : hours = "" + hours + "h ";
  1743.  
  1744. if (days > 0)
  1745. return '' + days + ' day' + ((days>1)? 's' : ' ') + hours;
  1746. return hours + minutes + seconds;
  1747. }
  1748.  
  1749. HITStorage.update_status_label = function(new_status, color)
  1750. {
  1751. var label = document.getElementById('status_label');
  1752. label.innerHTML = new_status;
  1753. label.style.color = color || 'black';
  1754. }
  1755.  
  1756. // validate input field dates
  1757. // Accept YYYY-MM-DD
  1758. HITStorage.validate_date = function(input)
  1759. {
  1760. date = input.value;
  1761.  
  1762. if (date.match(/^[01]\d\/[0123]\d\/20\d\d$/) != null)
  1763. {
  1764. var d = date.split('\/');
  1765. date = d[2] + '-' + d[0] + '-' + d[1];
  1766. input.value = date;
  1767. }
  1768.  
  1769. if (date.match(/^$|^20\d\d\-[01]\d\-[0123]\d$/) != null)
  1770. {
  1771. input.style.backgroundColor = 'white';
  1772. return true;
  1773. }
  1774. input.style.backgroundColor = 'pink';
  1775. return false;
  1776. }
  1777.  
  1778. HITStorage.validate_dates = function()
  1779. {
  1780. from = document.getElementById('from_date');
  1781. to = document.getElementById('to_date');
  1782.  
  1783. if (HITStorage.validate_date(from) && HITStorage.validate_date(to))
  1784. {
  1785. if (from.value > to.value && to.value != '')
  1786. {
  1787. alert('Invalid date!');
  1788. return false;
  1789. }
  1790.  
  1791. return true;
  1792. }
  1793. alert('Invalid date!');
  1794. return false;
  1795. }
  1796.  
  1797. HITStorage.start_search = function()
  1798. {
  1799. if (HITStorage.validate_dates() == false)
  1800. return;
  1801.  
  1802. HITStorage.update_status_label('Using local HIT database', 'green');
  1803.  
  1804. var options = {};
  1805. options.term = document.getElementById('search_term').value;
  1806. options.status = document.getElementById('status_select').value;
  1807. options.donut = document.getElementById('donut_select').value;
  1808. options.from_date = document.getElementById('from_date').value;
  1809. options.to_date = document.getElementById('to_date').value;
  1810. options.export_csv = document.getElementById('export_csv').checked;
  1811.  
  1812. HITStorage.disable_inputs();
  1813. setTimeout(function(){ HITStorage.do_search(options); }, 500);
  1814. }
  1815.  
  1816. HITStorage.disable_inputs = function()
  1817. {
  1818. document.getElementById('delete_button').disabled = true;
  1819. document.getElementById('search_button').disabled = true;
  1820. document.getElementById('update_button').disabled = true;
  1821. document.getElementById('overview_button').disabled = true;
  1822. document.getElementById('import_button').disabled = true;
  1823. document.getElementById('pending_button').disabled = true;
  1824. document.getElementById('status_button').disabled = true;
  1825. document.getElementById('from_date').disabled = true;
  1826. document.getElementById('to_date').disabled = true;
  1827. document.getElementById('search_term').disabled = true;
  1828. document.getElementById('status_select').disabled = true;
  1829. document.getElementById('donut_select').disabled = true;
  1830. }
  1831.  
  1832. HITStorage.enable_inputs = function()
  1833. {
  1834. document.getElementById('delete_button').disabled = false;
  1835. document.getElementById('search_button').disabled = false;
  1836. document.getElementById('update_button').disabled = false;
  1837. document.getElementById('overview_button').disabled = false;
  1838. document.getElementById('import_button').disabled = false;
  1839. document.getElementById('pending_button').disabled = false;
  1840. document.getElementById('status_button').disabled = false;
  1841. document.getElementById('from_date').disabled = false;
  1842. document.getElementById('to_date').disabled = false;
  1843. document.getElementById('search_term').disabled = false;
  1844. document.getElementById('status_select').disabled = false;
  1845. document.getElementById('donut_select').disabled = false;
  1846. }
  1847.  
  1848.  
  1849. HITStorage.do_search = function(options)
  1850. {
  1851. HITStorage.indexedDB.getHITs(options);
  1852.  
  1853. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 3000);
  1854.  
  1855. HITStorage.enable_inputs();
  1856. }
  1857.  
  1858. HITStorage.show_results = function(results)
  1859. {
  1860. resultsWindow = window.open();
  1861. resultsWindow.document.write("<html><head><title>Status Detail Search Results</title></head><body>\n");
  1862. resultsWindow.document.write("<h1>HITs matching your search:</h1>\n");
  1863. resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n');
  1864. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>Date</th><th>Requester</th><th>HIT Title</th><th>Reward</th><th>Status</th><th>Feedback</th></tr>\n');
  1865.  
  1866. var odd = true;
  1867. var sum = 0;
  1868. var sum_rejected = 0;
  1869. var sum_approved = 0;
  1870. var sum_pending = 0;
  1871.  
  1872.  
  1873. var new_day = false;
  1874.  
  1875. for (var i=0; i<results.length; i++) {
  1876. odd = !odd;
  1877. sum += results[i].reward;
  1878. if (results[i].status == 'Rejected')
  1879. sum_rejected += results[i].reward;
  1880. else if (results[i].status == 'Pending Approval')
  1881. sum_pending += results[i].reward;
  1882. else
  1883. sum_approved += results[i].reward;
  1884.  
  1885. if (i>0 && (results[i-1].date != results[i].date))
  1886. new_day = true;
  1887. else
  1888. new_day = false;
  1889. resultsWindow.document.write(HITStorage.format_hit_line(results[i], odd, HITStorage.status_color(results[i].status), new_day ));
  1890. }
  1891.  
  1892. resultsWindow.document.write('<tr style="background-color:lightgrey"><th></th><th></th><th></th><th>$' + sum.toFixed(2) + '</th><th></th><th></th></tr>\n');
  1893. resultsWindow.document.write("</table>");
  1894. resultsWindow.document.write("<p>Found " + results.length + " matching HITs. $" + sum_approved.toFixed(2) + " approved, " +
  1895. "$" + sum_rejected.toFixed(2) + " rejected and $" + sum_pending.toFixed(2) + " pending.</p>");
  1896. resultsWindow.document.write("</body></html>")
  1897. resultsWindow.document.close();
  1898. }
  1899.  
  1900. HITStorage.status_color = function(status)
  1901. {
  1902. var color = "green";
  1903.  
  1904. if (status.match("Pending Approval"))
  1905. color = "orange";
  1906. else if (status.match("Rejected"))
  1907. color = "red";
  1908.  
  1909. return color;
  1910. }
  1911.  
  1912. HITStorage.format_hit_line = function(hit, odd, status_color, new_day)
  1913. {
  1914. var line = '<tr style="background-color:';
  1915. if (odd)
  1916. line += '#f1f3eb;';
  1917. else
  1918. line += 'white;';
  1919. line += ' valign=top;';
  1920. if (new_day)
  1921. line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px">';
  1922. else
  1923. line += '">';
  1924.  
  1925. line += '<td>' + hit.date + '</td>';
  1926. if (hit.requesterLink != null)
  1927. line += '<td style="width:165px"><a href="' + hit.requesterLink + '" title="Contact this Requester">' + hit.requesterName + '</a></td>';
  1928. else
  1929. line += '<td style="width:165px">' + hit.requesterName + '</td>';
  1930. line += '<td style="width:213px">' + hit.title + '</td>';
  1931. line += '<td style="width:45px">$' + hit.reward.toFixed(2) + '</td>';
  1932. line += '<td style="color:' + status_color + '; width:55px">' + hit.status + '</td>';
  1933. line += '<td><div style="width:225px; overflow:hidden">' + hit.feedback + '</div></td>';
  1934. line += '</tr>\n';
  1935. return line;
  1936. }
  1937.  
  1938. HITStorage.show_pending_overview = function(results)
  1939. {
  1940. resultsWindow = window.open();
  1941. resultsWindow.document.write("<html><head><title>Summary of Pending HITs</title></head><body>\n");
  1942. resultsWindow.document.write("<h1>Summary of Pending HITs</h1>\n");
  1943. resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n');
  1944. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>requesterId</th><th>Requester</th><th></th><th>Pending</th><th>Rewards</th>\n');
  1945.  
  1946. // 'requesterId,requesterName,pending,reward';
  1947. var odd = false;
  1948. var sum = 0;
  1949. var pending = 0;
  1950.  
  1951. for (var i=0; i<results.length; i++) {
  1952. odd = !odd;
  1953. sum += results[i][3];
  1954. pending += results[i][2];
  1955. resultsWindow.document.write(HITStorage.format_pending_line(results[i], odd, i));
  1956. }
  1957.  
  1958. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' + results.length + ' different requesterIds</th><th></th><th></th><th style="text-align: right">' + pending + '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th>\n');
  1959. resultsWindow.document.write("</table>");
  1960. resultsWindow.document.write("</body></html>")
  1961. resultsWindow.document.close();
  1962.  
  1963. for (var i=0; i<results.length; i++)
  1964. {
  1965. resultsWindow.document.getElementById('id-' + i).addEventListener("click", search_func(results[i][0], 'requesterId'), false);
  1966. resultsWindow.document.getElementById('id2-' + i).addEventListener("click", show_requester_func(results[i][0]) , false);
  1967. }
  1968. }
  1969.  
  1970. HITStorage.show_status_overview = function(results, date)
  1971. {
  1972. resultsWindow = window.open();
  1973. resultsWindow.document.write("<html><head><title>Daily HIT stats</title></head><body>\n");
  1974. if (date)
  1975. resultsWindow.document.write("<h1>Daily HIT stats</h1>\n");
  1976. else
  1977. resultsWindow.document.write("<h1>Daily HIT stats (' + date + ')</h1>\n");
  1978. resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n');
  1979. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>Date</th><th>Submitted</th><th>Approved</th><th>Rejected</th><th>Pending</th><th>Earnings</th>\n');
  1980.  
  1981. var odd = false;
  1982. var sum = 0;
  1983. var submitted = 0;
  1984. var approved = 0;
  1985. var rejected = 0;
  1986. var pending = 0;
  1987. var new_month = false;
  1988.  
  1989. for (var i=results.length-1; i>=0; i--) {
  1990. odd = !odd;
  1991. sum += results[i].earnings;
  1992. submitted += results[i].submitted;
  1993. approved += results[i].approved;
  1994. rejected += results[i].rejected;
  1995. pending += results[i].pending;
  1996. if (i<results.length-1)
  1997. new_month = (results[i].date.substr(0,7) != results[i+1].date.substr(0,7));
  1998. resultsWindow.document.write(HITStorage.format_status_line(results[i], odd, new_month));
  1999. }
  2000.  
  2001. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' + results.length + ' days</th><th style="text-align: left">' + submitted +
  2002. '</th><th style="text-align: left">' + approved +
  2003. '</th><th style="text-align: left">' + rejected +
  2004. '</th><th style="text-align: left">' + pending +
  2005. '</th><th style="text-align: left">$' + sum.toFixed(2) + '</th>\n');
  2006. resultsWindow.document.write("</table>");
  2007. resultsWindow.document.write("</body></html>")
  2008. resultsWindow.document.close();
  2009.  
  2010. for (var i=0; i<results.length; i++)
  2011. resultsWindow.document.getElementById(results[i].date).addEventListener("click", search_func('', 'date', results[i].date, results[i].date), false);
  2012. }
  2013.  
  2014. HITStorage.show_requester_overview = function(results, date)
  2015. {
  2016. resultsWindow = window.open();
  2017. resultsWindow.document.write("<html><head><title>Requester Overview</title></head><body>\n");
  2018. if (date)
  2019. resultsWindow.document.write("<h1>Requester Overview " + date + "</h1>\n");
  2020. else
  2021. resultsWindow.document.write("<h1>Requester Overview</h1>\n");
  2022. resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;width:90%;margin-left:auto;margin-right:auto;">\n');
  2023. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>requesterId</th><th>Requester</th><th></th><th>HITs</th><th>Pending</th><th>Rewards</th><th colspan="2">Rejected</th></tr>\n');
  2024.  
  2025. // 'requesterId,requesterName,hits,pending,reward,rejected';
  2026. var odd = false;
  2027. var sum = 0;
  2028. var hits = 0;
  2029. var rejected = 0;
  2030. var pending = 0;
  2031. var new_day = false;
  2032. var top = true;
  2033. var dot_line;
  2034.  
  2035. for (var i=0; i<results.length; i++) {
  2036. odd = !odd;
  2037. sum += results[i][3];
  2038. hits += results[i][2];
  2039. rejected += results[i][4];
  2040. pending += results[i][5];
  2041. dot_line = false;
  2042. if (i==10)
  2043. {
  2044. dot_line = true;
  2045. top = false;
  2046. }
  2047. if (i>10 && results[i][3] == 0 && results[i-1][3] != 0)
  2048. dot_line = true;
  2049.  
  2050. resultsWindow.document.write(HITStorage.format_overview_line(results[i], odd, dot_line, top, i));
  2051. }
  2052.  
  2053. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' + results.length + ' different requesterIds</th>' +
  2054. '<th></th><th></th><th style="text-align: right">' + hits + '<th style="text-align: right">' + pending +
  2055. '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th><th style="text-align: right">' + rejected + '</th>' +
  2056. '<th style="text-align: right">' +
  2057. (rejected/hits*100).toFixed(2) + '%</th></tr>\n');
  2058. resultsWindow.document.write("</table>");
  2059. resultsWindow.document.write("<p>Reward includes all 'Paid' and 'Approved - Pending Payment' HITs. " +
  2060. "Reward does not include any bonuses.</p>");
  2061. resultsWindow.document.write("</body></html>")
  2062. resultsWindow.document.close();
  2063.  
  2064. for (var i=0; i<results.length; i++)
  2065. {
  2066. resultsWindow.document.getElementById('id-' + i).addEventListener("click", search_func(results[i][0], 'requesterId'), false);
  2067. resultsWindow.document.getElementById('id2-' + i).addEventListener("click", show_requester_func(results[i][0]) , false);
  2068. }
  2069. }
  2070.  
  2071. HITStorage.show_requester = function(results)
  2072. {
  2073. resultsWindow = window.open();
  2074. resultsWindow.document.write('<html><head><title>' + results[0].requesterName + '</title></head><body>\n');
  2075. resultsWindow.document.write('<h1>' + results[0].requesterName + ' (' + results[0].requesterId + ')</h1>\n');
  2076.  
  2077. resultsWindow.document.write('You have submitted ' + results.length + ' HITs for this requester. Earliest ' + results[results.length-1].date +
  2078. ', latest ' + results[0].date);
  2079.  
  2080. resultsWindow.document.write('<p><a href="https://www.mturk.com/mturk/searchbar?selectedSearchType=hitgroups&requesterId=' + results[0].requesterId + '">' +
  2081. 'Search HITs created by this requester</a></p>');
  2082.  
  2083.  
  2084. resultsWindow.document.write('<p><a href="http://turkopticon.differenceengines.com/' + results[0].requesterId + '">' +
  2085. 'See reviews about this requester on Turkopticon</a> or ');
  2086. resultsWindow.document.write('<a href="' + TO_report_link(results[0].requesterId,results[0].requesterName) + '">' +
  2087. 'review this requester on Turkopticon</a></p>');
  2088.  
  2089. var reward = 0;
  2090. var hits = 0;
  2091. var sum = 0;
  2092. var rejected = 0;
  2093. var approved = 0;
  2094. var pending = 0;
  2095. var all_rejected = 0;
  2096. var all_approved = 0;
  2097. var all_pending = 0;
  2098.  
  2099. resultsWindow.document.write('<table style="border: 1px solid black;border-collapse:collapse;margin-left:10px;margin-right:auto;">\n');
  2100. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>Month' +
  2101. '</th><th>Submitted' +
  2102. '</th><th>Approved' +
  2103. '</th><th>Rejected' +
  2104. '</th><th>Pending' +
  2105. '</th><th>Earnings</th></tr>\n');
  2106.  
  2107. for (var i=0; i<results.length; i++) {
  2108. hits++;
  2109. if (results[i].status == 'Rejected')
  2110. {
  2111. all_rejected++;
  2112. rejected++;
  2113. }
  2114. else if (results[i].status == 'Pending Approval')
  2115. {
  2116. all_pending++;
  2117. pending++;
  2118. }
  2119. else
  2120. {
  2121. all_approved++;
  2122. approved++;
  2123. sum += results[i].reward;
  2124. reward += results[i].reward;
  2125. }
  2126.  
  2127. if (i==results.length-1 || (i<results.length-1 && (results[i].date.substr(0,7) != results[i+1].date.substr(0,7))))
  2128. {
  2129. resultsWindow.document.write('<tr><td style="text-align: right">' + results[i].date.substr(0,7) +
  2130. '</td><td style="text-align: right">' + hits +
  2131. '</td><td style="text-align: right">' + approved +
  2132. '</td><td style="text-align: right">' + rejected +
  2133. '</td><td style="text-align: right">' + pending +
  2134. '</td><td style="text-align: right">$' + reward.toFixed(2) + '</td></tr>\n');
  2135. reward = 0;
  2136. hits = 0;
  2137. approved = 0;
  2138. rejected = 0;
  2139. pending = 0;
  2140. }
  2141. }
  2142. resultsWindow.document.write('<tr style="background-color:lightgrey"><th>' +
  2143. '</th><th style="text-align: right">' + results.length +
  2144. '</th><th style="text-align: right">' + all_approved +
  2145. '</th><th style="text-align: right">' + all_rejected +
  2146. '</th><th style="text-align: right">' + all_pending +
  2147. '</th><th style="text-align: right">$' + sum.toFixed(2) + '</th></tr>\n');
  2148. resultsWindow.document.write('</table>');
  2149.  
  2150. resultsWindow.document.write('<p>Rewards do not include any bonuses</p>');
  2151.  
  2152. resultsWindow.document.write("</body></html>");
  2153. resultsWindow.document.close();
  2154. }
  2155.  
  2156. function TO_report_link(requesterId, requesterName)
  2157. {
  2158. return 'http://turkopticon.differenceengines.com/report?requester[amzn_id]=' + requesterId +
  2159. '&requester[amzn_name]=' + encodeURI(requesterName.trim());
  2160. }
  2161.  
  2162. HITStorage.format_overview_line = function(req, odd, dot_line, top, i)
  2163. {
  2164. var color;
  2165. if (top)
  2166. color = (odd)? 'ffffe0;' : '#eee8aa;';
  2167. else
  2168. color = (odd)? 'white;' : '#f1f3eb;';
  2169. var line = '<tr style="background-color:' + color;
  2170. if (dot_line)
  2171. line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px';
  2172. line += '">';
  2173. line += '<td><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="id-' +
  2174. i + '">&gt;&gt;</button>' +
  2175. '<button type="button" title="Show details about requester" style="height: 16px;font-size: 8px; padding: 0px;" id="id2-' +
  2176. i + '">+</button> ' + req[0].trim() +
  2177. '</td>';
  2178. line += '<td><a title="Requesters Turkopticon page" target="_blank" href="http://turkopticon.differenceengines.com/' + req[0].trim() + '">[TO]</a> ';
  2179. line += req[1].trim() + '</td>';
  2180. line += '<td style="width: 50px"><a title="Report requester to Turkopticon" target="_blank" href="' + TO_report_link(req[0], req[1]) + '">[report]</a></td>';
  2181. line += '<td style="text-align: right">' + req[2] + '</td>';
  2182. line += '<td style="text-align: right">' + req[5] + '</td>';
  2183. line += '<td style="text-align: right">$' + req[3].toFixed(2) + '</td>';
  2184. var p = (req[4]/req[2]*100).toFixed(1);
  2185. var pc = (p>0)? 'red' : 'green';
  2186. line += '<td style="text-align: right; color:' + pc + ';">' + req[4] + '</td>';
  2187. line += '<td style="text-align: right; color:' + pc + ';">' + p + '%</td>';
  2188. line += '</tr>\n';
  2189. return line;
  2190. }
  2191.  
  2192. HITStorage.format_pending_line = function(req, odd, i)
  2193. {
  2194. console.log(req);
  2195. var color = (odd)? 'white;' : '#f1f3eb;';
  2196. var line = '<tr style="background-color:' + color;
  2197. line += '">';
  2198. line += '<td style="white-space: nowrap; width: 150px; margin-right: 10px;"><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="id-' +
  2199. i + '">&gt;&gt;&gt;</button>' +
  2200. '<button type="button" title="Show details about requester" style="height: 16px;font-size: 8px; padding: 0px;" id="id2-' +
  2201. i + '">+</button> ' + req[0].trim() + '</td>';
  2202. line += '<td><a title="Requesters Turkopticon page" target="_blank" href="http://turkopticon.differenceengines.com/' + req[0].trim() + '">[TO]</a> ';
  2203. line += req[1].trim() + '</td>';
  2204. line += '<td style="width: 50px"><a title="Report requester to Turkopticon" target="_blank" href="' + TO_report_link(req[0], req[1]) + '">[report]</a></td>';
  2205. line += '<td style="text-align: right">' + req[2] + '</td>';
  2206. line += '<td style="text-align: right">$' + req[3].toFixed(2) + '</td>';
  2207. line += '</tr>\n';
  2208. return line;
  2209. }
  2210.  
  2211. HITStorage.format_status_line = function(d, odd, new_month)
  2212. {
  2213. var color = (odd)? 'white;' : '#f1f3eb;';
  2214. var line = '<tr style="background-color:' + color;
  2215. if (new_month)
  2216. line += ' border: 0px dotted #000000; border-width: 2px 0px 0px 0px">';
  2217. else
  2218. line += '">';
  2219. line += '<td><button type="button" title="Show all HITs" style="height: 16px;font-size: 8px; padding: 0px;" id="' +
  2220. d.date + '">&gt;&gt;&gt;</button> ' + d.date + '</td>';
  2221. line += '<td>' + d.submitted + '</td>';
  2222. line += '<td>' + d.approved + '</td>';
  2223. line += '<td>' + d.rejected + '</td>';
  2224. line += '<td>' + d.pending + '</td>';
  2225. line += '<td>$' + d.earnings.toFixed(2) + '</td>';
  2226. line += '</tr>\n';
  2227. return line;
  2228. }
  2229.  
  2230. HITStorage.show_pending_overview_csv = function(results)
  2231. {
  2232. var csvData = [];
  2233. csvData.push(["requesterId","requesterName","pending","reward","\n"]);
  2234. for (var i=0; i<results.length; i++) {
  2235. csvData.push(HITStorage.format_pending_line_csv(results[i]));
  2236. }
  2237. var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"});
  2238. saveAs(blob, "pending_overview.csv");
  2239. }
  2240.  
  2241. HITStorage.format_pending_line_csv = function(req)
  2242. {
  2243. var line = [];
  2244. line.push(req[0].trim());
  2245. line.push('"' + req[1].trim() + '"');
  2246. line.push(req[2]);
  2247. line.push(req[3].toFixed(2));
  2248. line.push('\n');
  2249. return line;
  2250. }
  2251.  
  2252.  
  2253. HITStorage.show_requester_overview_csv = function(results)
  2254. {
  2255. var csvData = [];
  2256. csvData.push(['requesterId','requesterName','hits','reward','rejected','pending','\n']);
  2257. for (var i=0; i<results.length; i++) {
  2258. csvData.push(HITStorage.format_overview_line_csv(results[i]));
  2259. }
  2260. var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"});
  2261. saveAs(blob, "requester_overview.csv");
  2262. }
  2263.  
  2264. HITStorage.format_overview_line_csv = function(req)
  2265. {
  2266. var line = [];
  2267. line.push(req[0].trim());
  2268. line.push('"' + req[1].trim() + '"');
  2269. line.push(req[2]);
  2270. line.push(req[3].toFixed(2));
  2271. line.push(req[4]);
  2272. line.push(req[5]);
  2273. line.push('\n');
  2274. return line;
  2275. }
  2276.  
  2277. HITStorage.show_status_overview_csv = function(results)
  2278. {
  2279. var csvData = [];
  2280. csvData.push(['Date','Submitted','Approved','Rejected','Pending','Earnings','\n']);
  2281. for (var i=results.length-1; i>=0; i--) {
  2282. csvData.push(HITStorage.format_status_line_csv(results[i]));
  2283. }
  2284. var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"});
  2285. //location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData);
  2286. saveAs(blob, "status_overview.csv");
  2287. }
  2288.  
  2289. HITStorage.format_status_line_csv = function(d)
  2290. {
  2291. var line = [];
  2292. line.push('"' + d.date + '"');
  2293. line.push(d.submitted);
  2294. line.push(d.approved);
  2295. line.push(d.rejected);
  2296. line.push(d.pending);
  2297. line.push(d.earnings.toFixed(2));
  2298. line.push('\n');
  2299. return line;
  2300. }
  2301.  
  2302. HITStorage.export_csv = function(results)
  2303. {
  2304. var csvData = [];
  2305. csvData.push(['hitId','date','requesterName','requesterId','title','reward','status','feedback','\n']);
  2306. for (var i=0; i<results.length; i++) {
  2307. csvData.push(HITStorage.format_csv_line(results[i]));
  2308. }
  2309. var blob = new Blob(csvData, {type: "text/csv;charset=utf-8"});
  2310. //location.href='data:text/csv;charset=utf8,' + encodeURIComponent(csvData);
  2311. saveAs(blob, "hit_database.csv");
  2312. }
  2313.  
  2314. HITStorage.format_csv_line = function(hit)
  2315. {
  2316. var line = [];
  2317. line.push('"' + hit.hitId.trim() + '"');
  2318. line.push('"' + hit.date.trim() + '"');
  2319. line.push('"' + hit.requesterName.trim() + '"');
  2320. line.push('"' + hit.requesterId.trim() + '"');
  2321. line.push('"' + hit.title.trim() + '"');
  2322. line.push(hit.reward.toFixed(2));
  2323. line.push('"' + hit.status.trim().replace(/\&nbsp;/g,' ') + '"');
  2324. line.push('"' + hit.feedback.trim() + '"');
  2325. line.push('\n');
  2326. return line;
  2327. }
  2328.  
  2329. HITStorage.do_update = function(days_to_update)
  2330. {
  2331. if (DAYS_TO_FETCH.length<1)
  2332. {
  2333. HITStorage.check_update();
  2334. return;
  2335. }
  2336. HITStorage.update_status_label('Please wait: ' + progress_bar(days_to_update-DAYS_TO_FETCH.length, days_to_update) +
  2337. ' (' + (days_to_update-DAYS_TO_FETCH.length) + '/' + days_to_update + ')', 'red');
  2338.  
  2339. var hits = [];
  2340. setTimeout(function(){ HITStorage.getHITData( DAYS_TO_FETCH.shift(), hits, 1, days_to_update); }, 2000);
  2341. }
  2342.  
  2343. HITStorage.update_done = function()
  2344. {
  2345. HITStorage.update_status_label('Script monkeys have updated your local database', 'green');
  2346. setTimeout( function() { HITStorage.update_status_label("Search powered by non-amazonian script monkeys"); }, 5000);
  2347.  
  2348. HITStorage.enable_inputs();
  2349.  
  2350. localStorage['HITDB UPDATED'] = new Date().toString();
  2351.  
  2352. var e = document.getElementById('user_activities.date_column_header.tooltip').parentNode.parentNode.childNodes[2].childNodes[1].childNodes[1];
  2353. if (e != null && e.textContent.trim() == 'Today') {
  2354. var today = e.href.slice(-8);
  2355. today = convert_date(today);
  2356. HITStorage.indexedDB.get_todays_projected_earnings(today);
  2357. }
  2358. HITStorage.indexedDB.get_pending_approvals();
  2359. HITStorage.indexedDB.get_pending_payments();
  2360. }
  2361.  
  2362.  
  2363. HITStorage.update_database = function()
  2364. {
  2365. HITStorage.disable_inputs();
  2366.  
  2367. if (localStorage['HITDB TRY_EXTRA_DAYS'] == 'YES') {
  2368. DAYS_TO_FETCH = HITStorage.getAllAvailableDays(20);
  2369. delete localStorage['HITDB TRY_EXTRA_DAYS'];
  2370. }
  2371. else
  2372. {
  2373. DAYS_TO_FETCH = HITStorage.getAllAvailableDays();
  2374. }
  2375. DAYS_TO_FETCH_CHECK = DAYS_TO_FETCH.slice(0);
  2376.  
  2377. // remove extra days from checklist
  2378. for (var i=0; i<DAYS_TO_FETCH_CHECK.length; i++)
  2379. {
  2380. if (DAYS_TO_FETCH_CHECK[i].submitted == -1) {
  2381. DAYS_TO_FETCH_CHECK = DAYS_TO_FETCH_CHECK.slice(0,i);
  2382. break;
  2383. }
  2384. }
  2385.  
  2386. DAYS_TO_FETCH = DAYS_TO_FETCH_CHECK.slice(0);
  2387. HITStorage.update_status_label('Please wait: script monkeys are preparing to start working', 'red');
  2388. setTimeout(function(){ HITStorage.prepare_update_and_check_pending_payments(); }, 100);
  2389. }
  2390.  
  2391. HITStorage.show_overview = function()
  2392. {
  2393. if (HITStorage.validate_dates() == false)
  2394. return;
  2395. var options = {};
  2396. options.term = document.getElementById('search_term').value;
  2397. options.status = document.getElementById('status_select').value;
  2398. options.donut = document.getElementById('donut_select').value;
  2399. options.from_date = document.getElementById('from_date').value;
  2400. options.to_date = document.getElementById('to_date').value;
  2401. options.export_csv = document.getElementById('export_csv').checked;
  2402.  
  2403. HITStorage.update_status_label('Please wait: script monkeys are picking bananas ?', 'red');
  2404. HITStorage.disable_inputs();
  2405. HITStorage.indexedDB.requesterOverview(options);
  2406. }
  2407.  
  2408. HITStorage.show_pendings = function()
  2409. {
  2410. var options = {};
  2411. options.term = document.getElementById('search_term').value;
  2412. options.status = document.getElementById('status_select').value;
  2413. options.donut = document.getElementById('donut_select').value;
  2414. options.from_date = document.getElementById('from_date').value;
  2415. options.to_date = document.getElementById('to_date').value;
  2416. options.export_csv = document.getElementById('export_csv').checked;
  2417.  
  2418. HITStorage.update_status_label('Please wait: script monkeys are picking bananas ?', 'red');
  2419. HITStorage.disable_inputs();
  2420. HITStorage.indexedDB.pendingOverview(options);
  2421. }
  2422.  
  2423. HITStorage.show_status = function()
  2424. {
  2425. if (HITStorage.validate_dates() == false)
  2426. return;
  2427. var options = {};
  2428. options.term = document.getElementById('search_term').value;
  2429. options.status = document.getElementById('status_select').value;
  2430. options.donut = document.getElementById('donut_select').value;
  2431. options.from_date = document.getElementById('from_date').value;
  2432. options.to_date = document.getElementById('to_date').value;
  2433. options.export_csv = document.getElementById('export_csv').checked;
  2434.  
  2435. HITStorage.update_status_label('Please wait: script monkeys are picking bananas ?', 'red');
  2436. HITStorage.disable_inputs();
  2437. HITStorage.indexedDB.statusOverview(options);
  2438. }
  2439.  
  2440. var IMPORT_DIALOG = null;
  2441.  
  2442. function import_dialog()
  2443. {
  2444. if (IMPORT_DIALOG == null)
  2445. {
  2446. IMPORT_DIALOG = document.createElement('div');
  2447. IMPORT_DIALOG.style.display = 'block';
  2448.  
  2449. IMPORT_DIALOG.style.position = 'fixed';
  2450. IMPORT_DIALOG.style.width = '600px';
  2451. //IMPORT_DIALOG.style.height = '400px';
  2452. IMPORT_DIALOG.style.height = '90%';
  2453. IMPORT_DIALOG.style.left = '50%';
  2454. IMPORT_DIALOG.style.right = '50%';
  2455. IMPORT_DIALOG.style.margin = '-300px 0px 0px -300px';
  2456. //IMPORT_DIALOG.style.top = '400px';
  2457. IMPORT_DIALOG.style.bottom = '10px';
  2458. IMPORT_DIALOG.style.padding = '10px';
  2459. IMPORT_DIALOG.style.border = '2px';
  2460. IMPORT_DIALOG.style.textAlign = 'center';
  2461. IMPORT_DIALOG.style.verticalAlign = 'middle';
  2462. IMPORT_DIALOG.style.borderStyle = 'solid';
  2463. IMPORT_DIALOG.style.borderColor = 'black';
  2464. IMPORT_DIALOG.style.backgroundColor = 'white';
  2465. IMPORT_DIALOG.style.color = 'black';
  2466. IMPORT_DIALOG.style.zIndex = '100';
  2467.  
  2468. var table = document.createElement('table');
  2469. var input = document.createElement('textarea');
  2470. var input2 = document.createElement('input');
  2471. var label = document.createElement('label');
  2472. var label2 = document.createElement('label');
  2473.  
  2474. label.textContent = 'Paste CSV-file in the textarea below.';
  2475. label2.textContent = 'CVS separator: ';
  2476. input.style.width = '100%';
  2477. input.style.height = '90%';
  2478.  
  2479. input2.maxLength = '1';
  2480. input2.size = '1';
  2481. input2.defaultValue = ',';
  2482.  
  2483. var import_button = document.createElement('button');
  2484. import_button.textContent = 'Import HITs';
  2485. import_button.addEventListener("click", import_dialog_close_func(true, input, input2), false);
  2486. import_button.style.margin = '5px';
  2487. var cancel_button = document.createElement('button');
  2488. cancel_button.textContent = 'Cancel';
  2489. cancel_button.addEventListener("click", import_dialog_close_func(false, input, input2), false);
  2490. cancel_button.style.margin = '5px';
  2491.  
  2492. IMPORT_DIALOG.appendChild(label);
  2493. IMPORT_DIALOG.appendChild(document.createElement('br'));
  2494. IMPORT_DIALOG.appendChild(label2);
  2495. IMPORT_DIALOG.appendChild(input2);
  2496. IMPORT_DIALOG.appendChild(document.createElement('br'));
  2497. IMPORT_DIALOG.appendChild(input);
  2498. IMPORT_DIALOG.appendChild(document.createElement('br'));
  2499. IMPORT_DIALOG.appendChild(cancel_button);
  2500. IMPORT_DIALOG.appendChild(import_button);
  2501. document.body.appendChild(IMPORT_DIALOG);
  2502. }
  2503. else
  2504. {
  2505. IMPORT_DIALOG.style.display = 'block';
  2506. }
  2507. }
  2508.  
  2509.  
  2510. /*
  2511. * CSVToArray() function is taken from:
  2512. *
  2513. * Blog Entry:
  2514. * Ask Ben: Parsing CSV Strings With Javascript Exec() Regular Expression Command
  2515. *
  2516. * Author:
  2517. * Ben Nadel / Kinky Solutions
  2518. *
  2519. * Link:
  2520. * http://www.bennadel.com/index.cfm?event=blog.view&id=1504
  2521. *
  2522. * Date Posted:
  2523. * Feb 19, 2009 at 10:03 AM
  2524. */
  2525. // This will parse a delimited string into an array of
  2526. // arrays. The default delimiter is the comma, but this
  2527. // can be overriden in the second argument.
  2528. function CSVToArray( strData, strDelimiter ) {
  2529. // Check to see if the delimiter is defined. If not,
  2530. // then default to comma.
  2531. strDelimiter = (strDelimiter || ",");
  2532.  
  2533. // Create a regular expression to parse the CSV values.
  2534. var objPattern = new RegExp(
  2535. (
  2536. // Delimiters.
  2537. "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
  2538.  
  2539. // Quoted fields.
  2540. "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
  2541.  
  2542. // Standard fields.
  2543. "([^\"\\" + strDelimiter + "\\r\\n]*))"
  2544. ),
  2545. "gi"
  2546. );
  2547.  
  2548. // Create an array to hold our data. Give the array
  2549. // a default empty first row.
  2550. var arrData = [[]];
  2551.  
  2552. // Create an array to hold our individual pattern
  2553. // matching groups.
  2554. var arrMatches = null;
  2555.  
  2556.  
  2557. // Keep looping over the regular expression matches
  2558. // until we can no longer find a match.
  2559. while (arrMatches = objPattern.exec( strData )){
  2560.  
  2561. // Get the delimiter that was found.
  2562. var strMatchedDelimiter = arrMatches[ 1 ];
  2563.  
  2564. // Check to see if the given delimiter has a length
  2565. // (is not the start of string) and if it matches
  2566. // field delimiter. If id does not, then we know
  2567. // that this delimiter is a row delimiter.
  2568. if (
  2569. strMatchedDelimiter.length &&
  2570. (strMatchedDelimiter != strDelimiter)
  2571. ){
  2572.  
  2573. // Since we have reached a new row of data,
  2574. // add an empty row to our data array.
  2575. arrData.push( [] );
  2576.  
  2577. }
  2578.  
  2579.  
  2580. // Now that we have our delimiter out of the way,
  2581. // let's check to see which kind of value we
  2582. // captured (quoted or unquoted).
  2583. if (arrMatches[ 2 ]){
  2584.  
  2585. // We found a quoted value. When we capture
  2586. // this value, unescape any double quotes.
  2587. var strMatchedValue = arrMatches[ 2 ].replace(
  2588. new RegExp( "\"\"", "g" ),
  2589. "\""
  2590. );
  2591.  
  2592. } else {
  2593.  
  2594. // We found a non-quoted value.
  2595. var strMatchedValue = arrMatches[ 3 ];
  2596.  
  2597. }
  2598.  
  2599.  
  2600. // Now that we have our value string, let's add
  2601. // it to the data array.
  2602. arrData[ arrData.length - 1 ].push( strMatchedValue );
  2603. }
  2604.  
  2605. // Return the parsed data.
  2606. return( arrData );
  2607. }
  2608.  
  2609. function import_dialog_close_func(save, input, separator)
  2610. {
  2611. return function()
  2612. {
  2613. if (save == true)
  2614. {
  2615.  
  2616. var lines = [];
  2617. var hits = [];
  2618. var dates = [];
  2619.  
  2620. if (input.value.length > 0)
  2621. lines = CSVToArray(input.value, separator.value);
  2622.  
  2623. var errors = 0;
  2624. for (var i = 0; i<lines.length; i++)
  2625. {
  2626. var error = false;
  2627. try {
  2628. if (lines[i][0] == null || lines[i][0] == 'hitId')
  2629. continue;
  2630.  
  2631. if(lines[i][6] == 'Approved - Pending Payment')
  2632. lines[i][6] = 'Approved&nbsp;- Pending&nbsp;Payment';
  2633.  
  2634. if (lines[i].length != 8)
  2635. error = true;
  2636.  
  2637. var hit = {
  2638. hitId : lines[i][0],
  2639. date : convert_date(lines[i][1]),
  2640. requesterName : lines[i][2],
  2641. //This line was null in the version I was using. I added it in, giving it the proper format.
  2642. //This setting is for the links to contact the requester in the status window
  2643. requesterLink : "https://www.mturk.com/mturk/contact?subject=Regarding+Amazon+Mechanical+Turk+HIT+"+lines[i][0]+"&requesterId="+lines[i][3]+"&requesterName="+lines[i][2].replace(" ","+"),
  2644. requesterId : lines[i][3],
  2645. title : lines[i][4],
  2646. reward : parseFloat(lines[i][5]),
  2647. status : lines[i][6],
  2648. feedback : lines[i][7] || "" // If no feedback, put empty string
  2649. };
  2650. //This status thing is actually for the Hit Status page (daily overview). It was non-existent with the current version, I added the functionality in here.
  2651. //This sets up a simple associative array for the initial "date" entry for the hit stats. See below for implementation
  2652. var status = {
  2653. date : hit.date,
  2654. approved : (hit.status != "Rejected" ? 1 : 0),
  2655. earnings : (hit.status != "Rejected" ? hit.reward : 0),
  2656. pending : (hit.status != "Pending" ? 0 : 1),
  2657. rejected : (hit.status == "Rejected" ? 1 : 0),
  2658. submitted : 1
  2659. };
  2660. } catch(err) { error = true; }
  2661.  
  2662. if (error == false){
  2663. hits.push(hit);
  2664. //Implementation of status stuff. First I see if the object exists in my "dates" array,
  2665. var index = lookup(hit.date, "date", dates);
  2666. if (index != -1){
  2667. //if it does, add each value except date to update it. The values will either be 1 or 0, so just += should give the proper values (and it does based on testing
  2668. for (var key in dates[index]){
  2669. if (key != "date")
  2670. dates[index][key] += status[key];
  2671. }
  2672. }
  2673. else
  2674. dates.push(status); //if the date doesn't exist in the array, add it as an initial object
  2675. }
  2676. else
  2677. errors++;
  2678. }
  2679. if (hits.length < 1)
  2680. {
  2681. alert('No HITs found!');
  2682. return;
  2683. }
  2684. else if (confirm('Found ' + hits.length + ' HITs' + (errors>0? ' and ' + errors + (errors==1? ' error' : ' errors') : '') +
  2685. '.\nDo not reload this page until import is ready.\n' +
  2686. 'Press Ok to start.') == true)
  2687. {
  2688. HITStorage.disable_inputs();
  2689. HITStorage.update_status_label('Please wait: importing HITs', 'red');
  2690. IMPORT_DIALOG.style.display = 'none';
  2691. input.value = '';
  2692. HITStorage.indexedDB.importHITs(hits);
  2693. //You have to call updateHITstats on a date object:
  2694. //object = { date:"yyyy-mm-dd", (approved|pending|rejected):int num(Approved|Pending|Rejected), earnings:float totalEarningsForDay, submitted:int numHitsSubmittedThatDay }
  2695. //Easiest hack to do so, parse over the dates objects I manipulated above, call update hit stats on each of them.
  2696. for (var i = 0; i < dates.length; i++){
  2697. HITStorage.indexedDB.updateHITstats(dates[i]);
  2698. }
  2699. return;
  2700. }
  2701. else { return; }
  2702. }
  2703.  
  2704. IMPORT_DIALOG.style.display = 'none';
  2705. input.value = '';
  2706. };
  2707. }
  2708.  
  2709. //simple lookup function for searching I'm reusing.
  2710. function lookup (needle, key, haystack) {
  2711. for (var i = 0; i < haystack.length; i++){
  2712. if (haystack[i][key] == needle)
  2713. return i;
  2714. }
  2715. return -1;
  2716. }
  2717.  
  2718. function get_requester_id(s) {
  2719. var idx = 12 + s.search('requesterId=');
  2720. return s.substr(idx);
  2721. }
  2722.  
  2723. function show_requester_func(requesterId)
  2724. {
  2725. return function()
  2726. {
  2727. HITStorage.indexedDB.showRequester(requesterId);
  2728. };
  2729. }
  2730.  
  2731. function search_func(key, index, d1, d2)
  2732. {
  2733. d1 = d1 || '';
  2734. d2 = d2 || d1;
  2735. return function()
  2736. {
  2737. HITStorage.indexedDB.getHITs({term: key, index: index, status: '---', from_date: d1, to_date: d2, donut: '', this_day: ''});
  2738. };
  2739. }
  2740.  
  2741. function visible_func(element, visible)
  2742. {
  2743. return function()
  2744. {
  2745. element.style.visibility = (visible)? 'visible' : 'hidden';
  2746. };
  2747. }
  2748.  
  2749. function delete_func()
  2750. {
  2751. return function()
  2752. {
  2753. if (confirm('This will remove your local HIT DataBase!\nContinue?'))
  2754. {
  2755. HITStorage.indexedDB.deleteDB();
  2756. }
  2757. };
  2758. }
  2759.  
  2760. function import_func()
  2761. {
  2762. return function()
  2763. {
  2764. import_dialog();
  2765. };
  2766. }
  2767.  
  2768. function note_func(id, label)
  2769. {
  2770. return function()
  2771. {
  2772. note = prompt('Note for requesterId \'' + id + '\':', label.textContent);
  2773.  
  2774. if (note == null)
  2775. {
  2776. return;
  2777. }
  2778.  
  2779. HITStorage.indexedDB.addNote(id, note);
  2780. label.textContent = note;
  2781.  
  2782. label.style.border = '1px dotted';
  2783. if (note.indexOf('!') >= 0)
  2784. label.style.color = 'red';
  2785. else
  2786. label.style.color = 'black';
  2787. };
  2788. }
  2789.  
  2790. function block_func(requesterId, title, hitElement)
  2791. {
  2792. return function()
  2793. {
  2794. re = prompt('Block HITs from requesterId \'' + requesterId + '\' matching:\n' +
  2795. '(default matches only exactly same HIT title, leave empty to match all HITS)', '^'
  2796. + title.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1") + '$');
  2797.  
  2798. if (re == null)
  2799. {
  2800. return;
  2801. }
  2802. re = new RegExp(re);
  2803.  
  2804. if (!re.test(title)) {
  2805. if (confirm("Your regular expression does not match current HIT title.\nSave it anyway?") == false)
  2806. return;
  2807. }
  2808.  
  2809. HITStorage.indexedDB.addBlock(requesterId, re);
  2810. };
  2811. }
  2812.  
  2813. function unblock_func(requesterId, title)
  2814. {
  2815. return function()
  2816. {
  2817. var unblock = confirm('Unblocking removes all blocks that match this HITs title and requesterId.');
  2818. if (unblock == true)
  2819. {
  2820. HITStorage.indexedDB.removeBlocks(requesterId, title);
  2821. }
  2822. };
  2823. }
  2824.  
  2825. function auto_update_func()
  2826. {
  2827. return function()
  2828. {
  2829. var button = document.getElementById('auto_button');
  2830.  
  2831. if (localStorage['HITDB AUTO UPDATE'] === undefined)
  2832. {
  2833. alert('Enable Hit DataBase Auto Update\nWhen enabled, script will fetch last ' +
  2834. 'statusdetail pages and add them to database when this page is reloaded ' +
  2835. 'and at least two minutes have passed from last update. You still need to ' +
  2836. 'do full update from dashboard every now and then.');
  2837. button.textContent = 'Auto Update is ON';
  2838. button.style.color = 'green';
  2839. localStorage['HITDB AUTO UPDATE'] = 'ON';
  2840. }
  2841. else if (localStorage['HITDB AUTO UPDATE'] == 'ON')
  2842. {
  2843. button.textContent = 'Auto Update is OFF';
  2844. button.style.color = 'red';
  2845. localStorage['HITDB AUTO UPDATE'] = 'OFF';
  2846. }
  2847. else
  2848. {
  2849. button.textContent = 'Auto Update is ON';
  2850. button.style.color = 'green';
  2851. localStorage['HITDB AUTO UPDATE'] = 'ON';
  2852. }
  2853. };
  2854. }
  2855.  
  2856. function set_target_func(date)
  2857. {
  2858. return function()
  2859. {
  2860. var target = localStorage['TODAYS TARGET'];
  2861. if (target === undefined)
  2862. target = '';
  2863. else
  2864. target = parseFloat(localStorage['TODAYS TARGET']).toFixed(2);
  2865. target = prompt('Set your target:', target);
  2866.  
  2867. if (target == null)
  2868. return;
  2869. target = parseFloat(target);
  2870.  
  2871. localStorage['TODAYS TARGET'] = target.toFixed(2);
  2872. if (date != null)
  2873. HITStorage.indexedDB.get_todays_projected_earnings(date);
  2874. };
  2875. }
  2876.  
  2877. function random_face()
  2878. {
  2879. var faces = ['?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?'];
  2880. var n = Math.floor((Math.random()*faces.length));
  2881. return '<span style="color: black; font-weight: normal;" title="Featured non-amazonian script ' + ((n>11) ? '... kitten?': 'monkey') + '">' + faces[n] + '</span>';
  2882. }
  2883.  
  2884. function progress_bar(done, max, full, empty, c1, c2)
  2885. {
  2886. max = (max<1)? 1 : max;
  2887. done = (done<0)? 0 : done;
  2888. done = (done>max)? max : done;
  2889.  
  2890. var bar = '<span style="color: ' + (c1||'green') + '">';
  2891. for (var i=0; i<done; i++)
  2892. {
  2893. bar += full || '■';
  2894. }
  2895. bar += '</span><span style="color: ' + (c2||'black') + '">';
  2896. for (var i=done; i<max; i++)
  2897. {
  2898. bar += empty || '⬜';
  2899. }
  2900. bar += '</span>';
  2901. return bar;
  2902. }
  2903.  
  2904. // convert date to more practical form (MMDDYYYY => YYYY-MM-DD)
  2905. function convert_date(date)
  2906. {
  2907. if (date.indexOf('-') > 0)
  2908. return date;
  2909. var day = date.substr(2,2);
  2910. var month = date.substr(0,2);
  2911. var year = date.substr(4,4);
  2912. return (year + '-' + month + '-' + day);
  2913. }
  2914.  
  2915. // convert date from YYYY-MM-DD to MMDDYYYY if it isn't already
  2916. function convert_iso_date(date)
  2917. {
  2918. if (date.indexOf('-') < 0)
  2919. return date;
  2920. var t = date.split('-');
  2921. return t[1] + t[2] + t[0];
  2922. }
  2923.  
  2924. // Format date for display YYYY-MM-DD, DD/MM/YYYY or DD.MM.YYYY
  2925. function display_date(date, format)
  2926. {
  2927. if (format === undefined || format == null)
  2928. return date;
  2929.  
  2930. var d = date.split('-');
  2931.  
  2932. if (format == 'little')
  2933. {
  2934. return d[2] + '.' + d[1] + '.' + d[0];
  2935. }
  2936. if (format == 'middle')
  2937. {
  2938. return d[1] + '/' + d[2] + '/' + d[0];
  2939. }
  2940. }
  2941.  
  2942. HITStorage.indexedDB.create();
  2943.  
  2944. // Backup plan
  2945. //HITStorage.update_date_format(true);
  2946.  
  2947. if (document.location.href.match('https://www.mturk.com/mturk/dashboard'))
  2948. {
  2949. var footer = document.getElementsByClassName('footer_separator')[0];
  2950. if (footer == null)
  2951. return;
  2952.  
  2953. var extra_table = document.createElement('table');
  2954. extra_table.width = '700';
  2955. extra_table.style.boder = '1px solid black';
  2956. extra_table.align = 'center';
  2957. extra_table.cellSpacing = '0px';
  2958. extra_table.cellPadding = '0px';
  2959. var row1 = document.createElement('tr');
  2960. var row2 = document.createElement('tr');
  2961. var td1 = document.createElement('td');
  2962. var content_td = document.createElement('td');
  2963. var whatsthis = document.createElement('a');
  2964.  
  2965. row1.style.height = '25px';
  2966. td1.setAttribute('class', 'white_text_14_bold');
  2967. td1.style.backgroundColor = '#7fb448';//'#7fb4cf';
  2968. td1.style.paddingLeft = '10px';
  2969. td1.innerHTML = 'HIT DataBase' + random_face() + ' ';
  2970. content_td.setAttribute('class', 'container-content');
  2971.  
  2972. whatsthis.href = 'http://userscripts.org/scripts/show/149548';
  2973. whatsthis.setAttribute('class', 'whatis');
  2974. whatsthis.textContent = '(What\'s this?)';
  2975.  
  2976. extra_table.appendChild(row1);
  2977. row1.appendChild(td1);
  2978. td1.appendChild(whatsthis);
  2979. extra_table.appendChild(row2);
  2980. row2.appendChild(content_td);
  2981. footer.parentNode.insertBefore(extra_table, footer);
  2982.  
  2983. var my_bar = document.createElement('div');
  2984. var search_button = document.createElement('button');
  2985. var status_select = document.createElement('select');
  2986. var label = document.createElement('label');
  2987. var label2 = document.createElement('label');
  2988. var input = document.createElement('input');
  2989. var donut_select = document.createElement('select');
  2990. var csv_label = document.createElement('label');
  2991. var csv = document.createElement('input');
  2992.  
  2993. var update_button = document.createElement('button');
  2994. var delete_button = document.createElement('button');
  2995. var pending_button = document.createElement('button');
  2996. var overview_button = document.createElement('button');
  2997. var import_button = document.createElement('button');
  2998. var status_button = document.createElement('button');
  2999.  
  3000. var from_input = document.createElement('input');
  3001. var to_input = document.createElement('input');
  3002. var date_label1 = document.createElement('label');
  3003. var date_label2 = document.createElement('label');
  3004. date_label1.textContent = 'from date ';
  3005. date_label2.textContent = ' to ';
  3006. from_input.setAttribute('id', "from_date");
  3007. to_input.setAttribute('id', "to_date");
  3008. to_input.setAttribute('maxlength', "10");
  3009. from_input.setAttribute('maxlength', "10");
  3010. to_input.setAttribute('size', "10");
  3011. from_input.setAttribute('size', "10");
  3012. from_input.title = 'Date format YYYY-MM-DD\nOr leave empty.';
  3013. to_input.title = 'Date format YYYY-MM-DD\nOr leave empty.';
  3014.  
  3015. var donut_options = [];
  3016. donut_options[0] = document.createElement("option");
  3017. donut_options[1] = document.createElement("option");
  3018. donut_options[2] = document.createElement("option");
  3019. donut_options[0].text = "---";
  3020. donut_options[1].text = "Donut Chart HITS";
  3021. donut_options[2].text = "Donut Chart REWARDS";
  3022. donut_options[0].value = "---";
  3023. donut_options[1].value = "HITS";
  3024. donut_options[2].value = "REWARDS";
  3025.  
  3026. var status_options = [];
  3027. status_options[0] = document.createElement("option");
  3028. status_options[1] = document.createElement("option");
  3029. status_options[2] = document.createElement("option");
  3030. status_options[3] = document.createElement("option");
  3031. status_options[4] = document.createElement("option");
  3032. status_options[5] = document.createElement("option");
  3033. status_options[0].text = "Pending Approval";
  3034. status_options[0].style.color = "orange";
  3035. status_options[1].text = "Rejected";
  3036. status_options[1].style.color = "red";
  3037. status_options[2].text = "Approved - Pending Payment";
  3038. status_options[2].style.color = "green";
  3039. status_options[3].text = "Paid";
  3040. status_options[3].style.color = "green";
  3041. status_options[4].text = "Paid AND Approved";
  3042. status_options[4].style.color = "green";
  3043. status_options[5].text = "ALL";
  3044. status_options[0].value = "Pending Approval";
  3045. status_options[1].value = "Rejected";
  3046. status_options[2].value = "Approved";
  3047. status_options[3].value = "Paid";
  3048. status_options[4].value = "Paid|Approved";
  3049. status_options[5].value = "---";
  3050.  
  3051. search_button.setAttribute('id', "search_button");
  3052. input.setAttribute('id', "search_term");
  3053. status_select.setAttribute('id', "status_select");
  3054. label.setAttribute('id', "status_label");
  3055. donut_select.setAttribute('id', "donut_select");
  3056. delete_button.setAttribute('id', "delete_button");
  3057. update_button.setAttribute('id', "update_button");
  3058. overview_button.setAttribute('id', "overview_button");
  3059. import_button.setAttribute('id', "import_button");
  3060. pending_button.setAttribute('id', "pending_button");
  3061. status_button.setAttribute('id', "status_button");
  3062.  
  3063. my_bar.style.marginLeft = 'auto';
  3064. my_bar.style.marginRight = 'auto';
  3065. my_bar.style.textAlign = 'center';
  3066. label.style.marginLeft = 'auto';
  3067. label.style.marginRight = 'auto';
  3068. label.style.textAlign = 'center';
  3069.  
  3070. var donut = document.createElement('div');
  3071. donut.setAttribute('id', "container");
  3072. donut.style.display = 'none';
  3073.  
  3074. content_td.appendChild(my_bar);
  3075. my_bar.appendChild(delete_button);
  3076. my_bar.appendChild(import_button);
  3077. my_bar.appendChild(update_button);
  3078. my_bar.appendChild(document.createElement("br"));
  3079. my_bar.appendChild(pending_button);
  3080. my_bar.appendChild(overview_button);
  3081. my_bar.appendChild(status_button);
  3082. my_bar.appendChild(document.createElement("br"));
  3083. my_bar.appendChild(donut_select);
  3084. my_bar.appendChild(status_select);
  3085. my_bar.appendChild(label2);
  3086. my_bar.appendChild(input);
  3087. my_bar.appendChild(search_button);
  3088. my_bar.appendChild(document.createElement("br"));
  3089. my_bar.appendChild(date_label1);
  3090. my_bar.appendChild(from_input);
  3091. my_bar.appendChild(date_label2);
  3092. my_bar.appendChild(to_input);
  3093. my_bar.appendChild(csv_label);
  3094. my_bar.appendChild(csv);
  3095. my_bar.appendChild(document.createElement("br"));
  3096. my_bar.appendChild(label);
  3097. my_bar.appendChild(document.createElement("br"));
  3098. (footer.parentNode).insertBefore(donut, footer);
  3099.  
  3100. my_bar.style.textAlign = "float";
  3101. search_button.textContent = "Search";
  3102. search_button.title = "Search from local HIT database\nYou can set time limits and export as CSV-file";
  3103. label2.textContent = " HITs matching: ";
  3104. input.value = "";
  3105.  
  3106. label.textContent = "Search powered by non-amazonian script monkeys";
  3107.  
  3108. for (var i=0; i<status_options.length; i++)
  3109. status_select.options.add(status_options[i]);
  3110. for (var i=0; i<donut_options.length; i++)
  3111. donut_select.options.add(donut_options[i]);
  3112.  
  3113. update_button.title = "Fetch status pages and copy HITs to local indexed database.\nFirst time may take several minutes!";
  3114. update_button.textContent = "Update database";
  3115. update_button.style.color = 'green';
  3116. update_button.style.margin = '5px 5px 5px 5x';
  3117. delete_button.textContent = "Delete database";
  3118. delete_button.style.color = 'red';
  3119. delete_button.style.margin = '5px 5px 5px 5px';
  3120. delete_button.title = "Delete Local DataBase!";
  3121. import_button.textContent = "Import";
  3122. import_button.style.margin = '5px 5px 5px 5px';
  3123. import_button.title = "Import HIT data from exported CSV-file";
  3124. overview_button.textContent = "Requester Overview";
  3125. overview_button.style.margin = '0px 5px 5px 5px';
  3126. overview_button.title = "Summary of all requesters you have worked for\nYou can set time limit and export as CSV-file";
  3127. pending_button.textContent = "Pending Overview";
  3128. pending_button.style.margin = '0px 5px 5px 5px';
  3129. pending_button.title = "Summary of all pending HITs\nYou can export as CSV-file";
  3130. status_button.textContent = "Daily Overview";
  3131. status_button.style.margin = '0px 5px 5px 5px';
  3132. status_button.title = "Summary of each day you have worked on MTurk\nYou can set time limit and export as CSV-file";
  3133.  
  3134. pending_button.addEventListener("click", HITStorage.show_pendings, false);
  3135. overview_button.addEventListener("click", HITStorage.show_overview, false);
  3136. search_button.addEventListener("click", HITStorage.start_search, false);
  3137. update_button.addEventListener("click", HITStorage.update_database, false);
  3138. delete_button.addEventListener("click", delete_func(), false);
  3139. import_button.addEventListener("click", import_func(), false);
  3140. status_button.addEventListener("click", HITStorage.show_status, false);
  3141.  
  3142. csv_label.textContent = 'export CSV';
  3143. csv_label.title = 'Export results as comma-separated values';
  3144. csv_label.style.verticalAlign = 'middle';
  3145. csv_label.style.marginLeft = '50px';
  3146. csv.title = 'Export results as comma-separated values';
  3147. csv.setAttribute('type', 'checkbox');
  3148. csv.setAttribute('id', 'export_csv');
  3149. csv.style.verticalAlign = 'middle';
  3150.  
  3151. from_input.value = '';
  3152. to_input.value = '';
  3153.  
  3154. var table = document.getElementById('lnk_show_earnings_details');
  3155. if (table != null)
  3156. {
  3157. table = table.parentNode.parentNode.parentNode.parentNode;
  3158. var pending_tr = document.createElement('tr');
  3159. var pending_td1 = document.createElement('td');
  3160. var pending_td2 = document.createElement('td');
  3161. var today_tr = document.createElement('tr');
  3162. var today_td1 = document.createElement('td');
  3163. var today_td2 = document.createElement('td');
  3164.  
  3165. pending_tr.setAttribute('class', 'even');
  3166. pending_td1.setAttribute('class', 'metrics-table-first-value');
  3167. pending_td1.setAttribute('id', 'pending_earnings_header');
  3168. pending_td2.setAttribute('id', 'pending_earnings_value');
  3169. today_tr.setAttribute('class', 'odd');
  3170. today_td1.setAttribute('class', 'metrics-table-first-value');
  3171. today_td1.setAttribute('id', 'projected_earnings_header');
  3172. today_td2.setAttribute('id', 'projected_earnings_value');
  3173.  
  3174. pending_tr.appendChild(pending_td1);
  3175. pending_tr.appendChild(pending_td2);
  3176. today_tr.appendChild(today_td1);
  3177. today_tr.appendChild(today_td2);
  3178. table.appendChild(pending_tr);
  3179. table.appendChild(today_tr);
  3180.  
  3181. pending_td1.style.borderTop = '1px dotted darkgrey';
  3182. pending_td2.style.borderTop = '1px dotted darkgrey';
  3183. today_td1.style.borderBottom = '1px dotted darkgrey';
  3184. today_td2.style.borderBottom = '1px dotted darkgrey';
  3185.  
  3186. today_td1.title = 'This value can be inaccurate if HITDB has not been updated recently';
  3187. pending_td1.title = 'This value can be inaccurate if HITDB has not been updated recently';
  3188.  
  3189. if (localStorage['HITDB UPDATED'] === undefined)
  3190. pending_td1.textContent = 'Pending earnings';
  3191. else
  3192. pending_td1.textContent = 'Pending earnings (HITDB updated: ' + localStorage['HITDB UPDATED'] + ')';
  3193. today_td1.innerHTML = 'Projected earnings for today &nbsp;&nbsp;';
  3194. today_td2.textContent = 'ಠ_ಠ';
  3195. pending_td2.textContent = 'ಠ_ಠ';
  3196.  
  3197.  
  3198. var e = document.getElementById('user_activities.date_column_header.tooltip').parentNode.parentNode.childNodes[2].childNodes[1].childNodes[1];
  3199. var today = null;
  3200. if (e != null && e.textContent.trim() == 'Today') {
  3201. today = convert_date(e.href.slice(-8));
  3202. HITStorage.indexedDB.get_todays_projected_earnings(today);
  3203. }
  3204. HITStorage.indexedDB.get_pending_approvals();
  3205. HITStorage.indexedDB.get_pending_payments();
  3206.  
  3207. var target = document.createElement('span');
  3208. target.setAttribute('id', 'my_target');
  3209. target.textContent = 'click here to set your target';
  3210. target.style.fontSize = 'small';
  3211. target.style.color = 'blue';
  3212. today_td1.appendChild(target);
  3213. target.addEventListener("click", set_target_func(today), false);
  3214. }
  3215. }
  3216. else if (document.location.href.match('https://www.mturk.com/mturk/preview'))
  3217. {
  3218. var table = document.getElementById('requester.tooltip');
  3219. if (table == null)
  3220. return;
  3221. table = table.parentNode.parentNode.parentNode;
  3222. var title = table.parentNode.parentNode.parentNode.parentNode.getElementsByTagName('div')[0].textContent.trim();
  3223.  
  3224. var extra_row = document.createElement('tr');
  3225. var td_1 = document.createElement('td');
  3226. var td_2 = document.createElement('td');
  3227.  
  3228. var requesterId = document.getElementsByName('requesterId')[0].value;
  3229. var auto_approve = parseInt(document.getElementsByName('hitAutoAppDelayInSeconds')[0].value);
  3230.  
  3231. var buttons = [];
  3232. var b = ['Requester', 'HIT Title'];
  3233. for (var i=0; i<b.length; i++)
  3234. {
  3235. buttons[i] = document.createElement('button');
  3236. buttons[i].textContent = b[i];
  3237. buttons[i].id = b[i] + 'Button' + i;
  3238. buttons[i].style.fontSize = '10px';
  3239. buttons[i].style.height = '18px';
  3240. buttons[i].style.width = '80px';
  3241. buttons[i].style.border = '1px solid';
  3242. buttons[i].style.paddingLeft = '3px';
  3243. buttons[i].style.paddingRight = '3px';
  3244. buttons[i].style.backgroundColor = 'lightgrey';
  3245. buttons[i].setAttribute('form', 'NOThitForm');
  3246. td_1.appendChild(buttons[i]);
  3247. }
  3248. buttons[0].title = 'Search requester ' + requesterId + ' from HIT database';
  3249. buttons[1].title = 'Search title \'' + title + '\' from HIT database';
  3250.  
  3251. HITStorage.indexedDB.colorRequesterButton(requesterId, buttons[0]);
  3252. HITStorage.indexedDB.colorTitleButton(title, buttons[1]);
  3253. buttons[0].addEventListener("click", search_func(requesterId, 'requesterId'), false);
  3254. buttons[1].addEventListener("click", search_func(title, 'title'), false);
  3255.  
  3256. td_2.innerHTML = '<span class="capsule_field_title">Auto-Approval:</span>&nbsp&nbsp' + HITStorage.formatTime(auto_approve*1000) + '';
  3257. td_1.colSpan = '3';
  3258. td_2.colSpan = '8';
  3259.  
  3260. extra_row.appendChild(td_1);
  3261. extra_row.appendChild(td_2);
  3262. table.appendChild(extra_row);
  3263. }
  3264. else
  3265. {
  3266. for (var item=0; item<10; item++) {
  3267. var tooltip = document.getElementById('requester.tooltip--' + item);
  3268. if (tooltip == null)
  3269. break; // no need to continue
  3270. var titleElement = document.getElementById('capsule' + item + '-0');
  3271. var emptySpace = tooltip.parentNode.parentNode.parentNode.parentNode.parentNode;
  3272.  
  3273. var hitItem = tooltip.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;//.parentNode;
  3274.  
  3275. var requesterLabel = tooltip.parentNode;
  3276. var requesterId = tooltip.parentNode.parentNode.getElementsByTagName('a');
  3277. var title = titleElement.textContent.trim();
  3278.  
  3279. requesterId = get_requester_id(requesterId[1].href);
  3280.  
  3281. var buttons = [];
  3282. var row = document.createElement('tr');
  3283. var div = document.createElement('div');
  3284. emptySpace.appendChild(row);
  3285. row.appendChild(div);
  3286.  
  3287. /* Turkopticon link next to requester name */
  3288. //to_link = document.createElement('a');
  3289. //to_link.textContent = ' TO ';
  3290. //to_link.href = 'http://turkopticon.differenceengines.com/' + requesterId;
  3291. //to_link.target = '_blank';
  3292. //to_link.title = requesterId + ' on Turkopticon';
  3293. //tooltip.parentNode.parentNode.appendChild(to_link);
  3294. /*-----------------------------------------*/
  3295.  
  3296. HITStorage.indexedDB.blockHITS(requesterId, title, hitItem, titleElement);
  3297.  
  3298. var b = ['R', 'T', 'N', 'B'];
  3299. for (var i=0; i<b.length; i++)
  3300. {
  3301. buttons[i] = document.createElement('button');
  3302. buttons[i].textContent = b[i];
  3303. buttons[i].id = b[i] + 'Button' + i;
  3304. buttons[i].style.height = '18px';
  3305. buttons[i].style.fontSize = '10px';
  3306. buttons[i].style.border = '1px solid';
  3307. buttons[i].style.paddingLeft = '3px';
  3308. buttons[i].style.paddingRight = '3px';
  3309. buttons[i].style.backgroundColor = 'lightgrey';
  3310. div.appendChild(buttons[i]);
  3311. }
  3312. buttons[0].title = 'Search requester ' + requesterId + ' from HIT database';
  3313. buttons[1].title = 'Search title \'' + title + '\' from HIT database';
  3314. buttons[2].title = 'Add a requester note';
  3315. buttons[3].title = '"Block" requester';
  3316.  
  3317. var notelabel = document.createElement('label');
  3318. notelabel.textContent = '';
  3319. notelabel.id = b[i] + 'notelabel' + item;
  3320. notelabel.style.height = '18px';
  3321. notelabel.style.fontSize = '10px';
  3322. notelabel.style.marginLeft = '10px';
  3323. notelabel.style.padding = '1px';
  3324. notelabel.style.backgroundColor = 'transparent';
  3325. HITStorage.indexedDB.updateNoteButton(requesterId, notelabel);
  3326. div.appendChild(notelabel);
  3327.  
  3328. HITStorage.indexedDB.colorRequesterButton(requesterId, buttons[0]);
  3329. HITStorage.indexedDB.colorTitleButton(title, buttons[1]);
  3330. buttons[0].addEventListener("click", search_func(requesterId, 'requesterId'), false);
  3331. buttons[1].addEventListener("click", search_func(title, 'title'), false);
  3332. buttons[2].addEventListener("click", note_func(requesterId, notelabel), false);
  3333. buttons[3].addEventListener("click", block_func(requesterId, title, hitItem), false);
  3334.  
  3335. div.style.margin = "0px";
  3336.  
  3337. buttons[2].style.visibility = "hidden"; // "visible"
  3338. buttons[2].parentNode.addEventListener("mouseover", visible_func(buttons[2], true), false);
  3339. buttons[2].parentNode.addEventListener("mouseout", visible_func(buttons[2], false), false);
  3340. buttons[2].addEventListener("mouseout", visible_func(buttons[2], false), false);
  3341. buttons[3].style.visibility = "hidden"; // "visible"
  3342. buttons[3].parentNode.addEventListener("mouseover", visible_func(buttons[3], true), false);
  3343. buttons[3].parentNode.addEventListener("mouseout", visible_func(buttons[3], false), false);
  3344. buttons[3].addEventListener("mouseout", visible_func(buttons[3], false), false);
  3345. }
  3346.  
  3347. var auto_button = document.createElement('button');
  3348. auto_button.setAttribute('id', 'auto_button');
  3349. auto_button.title = 'HIT DataBase Auto Update\nAutomagically update newest HITs to database when reloading this page';
  3350.  
  3351. if (localStorage['HITDB AUTO UPDATE'] === undefined)
  3352. {
  3353. auto_button.textContent = 'Auto update ?';
  3354. auto_button.style.color = 'red';
  3355. }
  3356. else if (localStorage['HITDB AUTO UPDATE'] == 'ON')
  3357. {
  3358. auto_button.textContent = 'Auto Update is ON';
  3359. auto_button.style.color = 'green';
  3360. }
  3361. else
  3362. {
  3363. auto_button.textContent = 'Auto Update is OFF';
  3364. auto_button.style.color = 'red';
  3365. }
  3366.  
  3367. //var element = document.body.childNodes[13].childNodes[3].childNodes[1].childNodes[0].childNodes[5];
  3368. var element = document.getElementsByName("/sort")[0];
  3369. //element.insertBefore(auto_button, element.firstChild);
  3370. element.parentNode.insertBefore(auto_button, element.nextSibling);
  3371. auto_button.addEventListener("click", auto_update_func(), false);
  3372.  
  3373. setTimeout(HITStorage.getLatestHITs, 100);
  3374. }