diff --git a/README.md b/README.md index d86fd53..9ccb672 100644 --- a/README.md +++ b/README.md @@ -1 +1,39 @@ -# worker-activity-logger +# ReLauncher | Worker Activity Logger + +#### Setup worker browser logs collection for your tasks in 5 steps: + +1. fork this repository, +2. create a bucket in [Firebase](https://www.firebase.com/) (it is free), +3. insert your firebase bucket name in [logger.js](https://github.com/ReLauncher/worker-activity-logger/blob/gh-pages/logger.js#L17): + + ```javascript + var settings = { + debug: true, + firebase: { + bucket: "YOUR_FIREBASE_BUCKET_NAME" + } + } + ``` +4. [create a job](https://success.crowdflower.com/hc/en-us/articles/204056975-Getting-Started-on-CrowdFlower-An-Overview-to-Building-a-Job) in CrowdFlower, upload your data. Make sure you have a column, which can be used as an identificator of a given data row (unit). If you collect tags for images, it can be *image_url* +5. add the following in [CML](https://success.crowdflower.com/hc/en-us/articles/202817989-CML-CrowdFlower-Markup-Language-Overview) of your CrowdFlower job: + + ```html + + + ``` + +When you launch the job you should see logs appearing in your firebase bucket. If you do not - check all the steps carefully. If it still does not work, create an *issue* in this repository. + +- [ ] run unit_references_maker.js (once) +- [ ] run queue_management.js (keep it running during execution) +- [ ] run ReLauncher server (npm start) +- [ ] launch the job +- [ ] start ReLauncher for this job + +[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) diff --git a/app.json b/app.json new file mode 100644 index 0000000..dadc8c5 --- /dev/null +++ b/app.json @@ -0,0 +1,17 @@ +{ + "name": "Relauncher ReferenceMaker", + "description": "Aapplication listening to a given Firebase bucket and fixing unit references.", + "repository": "https://github.com/ReLauncher/worker-activity-logger", + "logo": "https://avatars3.githubusercontent.com/u/14817691?v=3&s=200", + "keywords": ["relauncher", "crowdflower", "crowdsourcing", "microtasks"], + "env": { + "HEROKU_URL": { + "description": "The URL of this Heroku app.", + "value": "http://YOURAPPNAME.herokuapp.com" + }, + "CROWDFLOWER_API_KEY":{ + "description" : "your API key from https://make.crowdflower.com/account/user", + "value":"" + } + } +} diff --git a/index.html b/example.html similarity index 75% rename from index.html rename to example.html index e6ba478..a55e350 100644 --- a/index.html +++ b/example.html @@ -1,8 +1,16 @@ + - - + + +
@@ -18,4 +26,5 @@

CrowdFlower Launcher

+ diff --git a/firebase_to_csv.js b/firebase_to_csv.js new file mode 100644 index 0000000..088b362 --- /dev/null +++ b/firebase_to_csv.js @@ -0,0 +1,101 @@ +var requestify = require('requestify'); +var fs = require('fs'); + + +var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +var task_id = 837900; +var firebase_target_url = firebase_base_url + task_id + ".json"; +var folder = "logs/"; +var filename = folder + task_id + "_logs.csv"; + +fs.createWriteStream(filename); +fs.truncate(filename, 0, function() { + console.log('file ' + filename + ' was cleaned up.') +}); +fs.appendFile(filename, 'unit_number, unit_id, assignment_id, dt_start, dt_end, status\n', function(err) {}); + +var Logs = []; +var Units = {}; +var MaxUnit = 0; +// ------------------------------------------------------- +// Convert log object into a string +// ------------------------------------------------------- +function stringify(log_array) { + for (var i = 0; i < log_array.length; i++) { + log_array[i]['string'] = ""; + log_array[i]['string'] += log_array[i].unit_number + ", "; + log_array[i]['string'] += log_array[i].unit_id + ", "; + log_array[i]['string'] += log_array[i].assignment_id + ", "; + //log_array[i]['string'] += log_array[i].worker_id + ", "; + log_array[i]['string'] += log_array[i].dt_start + ", "; + log_array[i]['string'] += log_array[i].dt_end + ", "; + log_array[i]['string'] += log_array[i].status; + } + return log_array; +} +// ------------------------------------------------------- +// Collect data for a given Job and make a plain Array of log objects +// ------------------------------------------------------- +requestify.get(firebase_target_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var assignments = response.getBody(); + for (var a_id in assignments) { + var a = assignments[a_id]; + if (a_id != "editor_preview" && a.unit_id) { + var unit_number; + if (Units[a.unit_id.toString()]) { + unit_number = Units[a.unit_id.toString()]; + } else { + MaxUnit++; + Units[a.unit_id.toString()] = MaxUnit; + unit_number = Units[a.unit_id.toString()]; + } + + + + //console.log(a); + for (var l_id in a.logs) { + var l = a.logs[l_id]; + var log_record = { + 'unit_number': unit_number, + 'unit_id': a.unit_id, + 'assignment_id': a_id, + //'worker_id': a.worker_id || "0", + 'dt_start': l.dt, + 'status': l.status + } + Logs.push(log_record); + } + } else { + console.log("PROBLEMATIC ASSIGNMENT", a_id, a); + } + } + // ------------------------------------------------------- + // Add end events to log objects + // ------------------------------------------------------- + for (var i = 0; i < Logs.length; i++) { + if (Logs[i].status != 'closed') { + if (i < (Logs.length - 1) && Logs[i].assignment_id == Logs[i + 1].assignment_id) { + Logs[i]['dt_end'] = Logs[i + 1]['dt_start']; + } + } + if (Logs[i]['dt_end'] == undefined) { + Logs[i]['dt_end'] = Logs[i]['dt_start'] + 1000; + } + + } + // ------------------------------------------------------- + // Convert each object in array into string and save it into the log file + // ------------------------------------------------------- + Logs = stringify(Logs); + //console.log(Logs.length); + for (var i = 0; i < Logs.length; i++) { + console.log(Logs[i]['string']); + fs.appendFile(filename, Logs[i]['string'] + '\n', function(err) {}); + } + console.log(firebase_target_url); + }); diff --git a/generators/clicks_to_csv.js b/generators/clicks_to_csv.js new file mode 100644 index 0000000..103e708 --- /dev/null +++ b/generators/clicks_to_csv.js @@ -0,0 +1,94 @@ +var requestify = require('requestify'); +var fs = require('fs'); + + +var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +var task_id = process.argv[2]; +var firebase_target_url = firebase_base_url + task_id + ".json"; +var folder = "../logs/"+task_id+"/"; +var filename = folder + task_id + "_clicks.csv"; + +fs.createWriteStream(filename); +fs.truncate(filename, 0, function() { + console.log('file ' + filename + ' was cleaned up.') +}); +fs.appendFile(filename, 'task_id, unit_id, assignment_id, session_id, dt_start, element\n', function(err) {}); + +var LOGS = []; + +var MaxUnit = 0; +// ------------------------------------------------------- +// Convert log object into a string +// ------------------------------------------------------- +function stringify(log_array) { + for (var i = 0; i < log_array.length; i++) { + log_array[i]['string'] = ""; + log_array[i]['string'] += log_array[i].task_id + ", "; + log_array[i]['string'] += log_array[i].unit_id + ", "; + log_array[i]['string'] += log_array[i].assignment_id + ", "; + log_array[i]['string'] += log_array[i].session_id + ", "; + log_array[i]['string'] += log_array[i].dt_start + ", "; + log_array[i]['string'] += log_array[i].element; + } + return log_array; +} + +function endify(log_array) { + var Logs = log_array; + for (var i = 0; i < Logs.length; i++) { + if (Logs[i].status != 'closed') { + if (i < (Logs.length - 1) && Logs[i].assignment_id == Logs[i + 1].assignment_id && Logs[i].session_id == Logs[i + 1].session_id) { + Logs[i]['dt_end'] = Logs[i + 1]['dt_start']; + } + } + if (Logs[i]['dt_end'] == undefined) { + Logs[i]['dt_end'] = Logs[i]['dt_start'] + 1000; + } + + } + return Logs; +} +// ------------------------------------------------------- +// Collect data for a given Job and make a plain Array of log objects +// ------------------------------------------------------- +requestify.get(firebase_target_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var job_logs = response.getBody(); + var units = job_logs.units + for (var unit_id in units) { + var assignments = units[unit_id]['assignments']; + for (var assignment_id in assignments) { + var sessions = assignments[assignment_id]['sessions']; + for (var session_id in sessions) { + var logs = sessions[session_id]['clicks']; + //console.log(logs); + for (var log_id in logs) { + var log_record = { + task_id: task_id, + unit_id: unit_id, + assignment_id: assignment_id, + session_id: session_id, + dt_start: logs[log_id].dt, + element: logs[log_id].element + }; + LOGS.push(log_record); + } + } + } + } + //LOGS = endify(LOGS); + LOGS = stringify(LOGS); + console.log(LOGS.length); + var all_text = ""; + for (var i = 0; i < LOGS.length; i++) { + all_text+=LOGS[i]['string'] + '\n'; + } + fs.appendFile(filename, all_text, function(err) { + if (err) + console.log(err); + }); + }); diff --git a/generators/generate_logs.sh b/generators/generate_logs.sh new file mode 100644 index 0000000..5d787c1 --- /dev/null +++ b/generators/generate_logs.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +echo "====================================" +echo "= FIREBASE CROWDFLOWER LOGS TO CSV =" +echo "====================================" + +echo "\nNow we create a folder for $@ task" +mkdir "../logs/$@" +echo "\nNow we generate logs of key presses from firebase..." +node key_presses_to_csv.js $@ +echo "\nNow we generate logs of clicks from firebase..." +node clicks_to_csv.js $@ +echo "\nNow we generate logs of page activity from firebase..." +node page_activity_to_csv.js $@ +echo "\nNow we generate logs of tab visibility from firebase..." +node tab_visibility_to_csv.js $@ + +echo "\nNow we copy the folder with logs to stats-and-graphs folder to be further processed" +cp -r "../logs/$@" "../../stats-and-graphs/logs/$@" + +echo "\nDONE" +echo "====================================" \ No newline at end of file diff --git a/generators/key_presses_to_csv.js b/generators/key_presses_to_csv.js new file mode 100644 index 0000000..996d239 --- /dev/null +++ b/generators/key_presses_to_csv.js @@ -0,0 +1,94 @@ +var requestify = require('requestify'); +var fs = require('fs'); + + +var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +var task_id = process.argv[2]; +var firebase_target_url = firebase_base_url + task_id + ".json"; +var folder = "../logs/"+task_id+"/"; +var filename = folder + task_id + "_keys.csv"; + +fs.createWriteStream(filename); +fs.truncate(filename, 0, function() { + console.log('file ' + filename + ' was cleaned up.') +}); +fs.appendFile(filename, 'task_id, unit_id, assignment_id, session_id, dt_start, key\n', function(err) {}); + +var LOGS = []; + +var MaxUnit = 0; +// ------------------------------------------------------- +// Convert log object into a string +// ------------------------------------------------------- +function stringify(log_array) { + for (var i = 0; i < log_array.length; i++) { + log_array[i]['string'] = ""; + log_array[i]['string'] += log_array[i].task_id + ", "; + log_array[i]['string'] += log_array[i].unit_id + ", "; + log_array[i]['string'] += log_array[i].assignment_id + ", "; + log_array[i]['string'] += log_array[i].session_id + ", "; + log_array[i]['string'] += log_array[i].dt_start + ", "; + log_array[i]['string'] += log_array[i].key; + } + return log_array; +} + +function endify(log_array) { + var Logs = log_array; + for (var i = 0; i < Logs.length; i++) { + if (Logs[i].status != 'closed') { + if (i < (Logs.length - 1) && Logs[i].assignment_id == Logs[i + 1].assignment_id && Logs[i].session_id == Logs[i + 1].session_id) { + Logs[i]['dt_end'] = Logs[i + 1]['dt_start']; + } + } + if (Logs[i]['dt_end'] == undefined) { + Logs[i]['dt_end'] = Logs[i]['dt_start'] + 1000; + } + + } + return Logs; +} +// ------------------------------------------------------- +// Collect data for a given Job and make a plain Array of log objects +// ------------------------------------------------------- +requestify.get(firebase_target_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var job_logs = response.getBody(); + var units = job_logs.units + for (var unit_id in units) { + var assignments = units[unit_id]['assignments']; + for (var assignment_id in assignments) { + var sessions = assignments[assignment_id]['sessions']; + for (var session_id in sessions) { + var logs = sessions[session_id]['key_pressed']; + //console.log(logs); + for (var log_id in logs) { + var log_record = { + task_id: task_id, + unit_id: unit_id, + assignment_id: assignment_id, + session_id: session_id, + dt_start: logs[log_id].dt, + key: logs[log_id].key + }; + LOGS.push(log_record); + } + } + } + } + //LOGS = endify(LOGS); + LOGS = stringify(LOGS); + console.log(LOGS.length); + var all_text = ""; + for (var i = 0; i < LOGS.length; i++) { + all_text+=LOGS[i]['string'] + '\n'; + } + fs.appendFile(filename, all_text, function(err) { + if (err) + console.log(err); + }); + }); diff --git a/generators/page_activity_to_csv.js b/generators/page_activity_to_csv.js new file mode 100644 index 0000000..ef28105 --- /dev/null +++ b/generators/page_activity_to_csv.js @@ -0,0 +1,103 @@ +var requestify = require('requestify'); +var fs = require('fs'); + + +var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +var task_id = process.argv[2]; +var firebase_target_url = firebase_base_url + task_id + ".json"; +var folder = "../logs/"+task_id+"/"; +var filename = folder + task_id + "_page.csv"; + +fs.createWriteStream(filename); +fs.truncate(filename, 0, function() { + console.log('file ' + filename + ' was cleaned up.') +}); +fs.appendFile(filename, 'task_id, unit_id, assignment_id, session_id, dt_start,dt_end, keyboard, mouse, scroll, scroll_top, text_selected\n', function(err) {}); + +var LOGS = []; + +var MaxUnit = 0; +// ------------------------------------------------------- +// Convert log object into a string +// ------------------------------------------------------- +function stringify(log_array) { + for (var i = 0; i < log_array.length; i++) { + log_array[i]['string'] = ""; + log_array[i]['string'] += log_array[i].task_id + ", "; + log_array[i]['string'] += log_array[i].unit_id + ", "; + log_array[i]['string'] += log_array[i].assignment_id + ", "; + log_array[i]['string'] += log_array[i].session_id + ", "; + log_array[i]['string'] += log_array[i].dt_start + ", "; + log_array[i]['string'] += log_array[i].dt_end + ", "; + log_array[i]['string'] += log_array[i].keyboard + ", "; + log_array[i]['string'] += log_array[i].mouse + ", "; + log_array[i]['string'] += log_array[i].scroll + ", "; + log_array[i]['string'] += log_array[i].scroll_top + ", "; + log_array[i]['string'] += log_array[i].text_selected; + } + return log_array; +} + +function endify(log_array) { + var Logs = log_array; + for (var i = 0; i < Logs.length; i++) { + if (Logs[i].status != 'closed') { + if (i < (Logs.length - 1) && Logs[i].assignment_id == Logs[i + 1].assignment_id && Logs[i].session_id == Logs[i + 1].session_id) { + Logs[i]['dt_end'] = Logs[i + 1]['dt_start']; + } + } + if (Logs[i]['dt_end'] == undefined) { + Logs[i]['dt_end'] = Logs[i]['dt_start'] + 1000; + } + + } + return Logs; +} +// ------------------------------------------------------- +// Collect data for a given Job and make a plain Array of log objects +// ------------------------------------------------------- +requestify.get(firebase_target_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var job_logs = response.getBody(); + var units = job_logs.units + for (var unit_id in units) { + var assignments = units[unit_id]['assignments']; + for (var assignment_id in assignments) { + var sessions = assignments[assignment_id]['sessions']; + for (var session_id in sessions) { + var logs = sessions[session_id]['page_activity']; + //console.log(logs); + for (var log_id in logs) { + var log_record = { + task_id: task_id, + unit_id: unit_id, + assignment_id: assignment_id, + session_id: session_id, + dt_start: logs[log_id].dt, + keyboard: logs[log_id].keyboard, + mouse: logs[log_id].mouse, + scroll: logs[log_id].scroll, + scroll_top: Math.round(logs[log_id].scroll_top), + text_selected: logs[log_id].text_selected + }; + LOGS.push(log_record); + } + } + } + } + LOGS = endify(LOGS); + LOGS = stringify(LOGS); + console.log(LOGS.length); + var all_text = ""; + for (var i = 0; i < LOGS.length; i++) { + all_text+=LOGS[i]['string'] + '\n'; + } + fs.appendFile(filename, all_text, function(err) { + if (err) + console.log(err); + }); + }); diff --git a/generators/tab_visibility_to_csv.js b/generators/tab_visibility_to_csv.js new file mode 100644 index 0000000..0fa352d --- /dev/null +++ b/generators/tab_visibility_to_csv.js @@ -0,0 +1,133 @@ +var requestify = require('requestify'); +var fs = require('fs'); + + +var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +var task_id = process.argv[2]; +var firebase_target_url = firebase_base_url + task_id + ".json"; +var folder = "../logs/"+task_id+"/"; +var filename = folder + task_id + "_tabs.csv"; + +fs.createWriteStream(filename); +fs.truncate(filename, 0, function() { + console.log('file ' + filename + ' was cleaned up.') +}); +fs.appendFile(filename, 'task_id, unit_id, assignment_id, session_id, dt_start,dt_end, status\n', function(err) {}); + +var LOGS = []; + +var MaxUnit = 0; + +function dynamicSort(property) { + var sortOrder = 1; + if(property[0] === "-") { + sortOrder = -1; + property = property.substr(1); + } + return function (a,b) { + var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; + return result * sortOrder; + } +} +function dynamicSortMultiple() { + /* + * save the arguments object as it will be overwritten + * note that arguments object is an array-like object + * consisting of the names of the properties to sort by + */ + var props = arguments; + return function (obj1, obj2) { + var i = 0, result = 0, numberOfProperties = props.length; + /* try getting a different result from 0 (equal) + * as long as we have extra properties to compare + */ + while(result === 0 && i < numberOfProperties) { + result = dynamicSort(props[i])(obj1, obj2); + i++; + } + return result; + } +} +// ------------------------------------------------------- +// Convert log object into a string +// ------------------------------------------------------- +function stringify(log_array) { + for (var i = 0; i < log_array.length; i++) { + log_array[i]['string'] = ""; + log_array[i]['string'] += log_array[i].task_id + ", "; + log_array[i]['string'] += log_array[i].unit_id + ", "; + log_array[i]['string'] += log_array[i].assignment_id + ", "; + log_array[i]['string'] += log_array[i].session_id + ", "; + log_array[i]['string'] += log_array[i].dt_start + ", "; + log_array[i]['string'] += log_array[i].dt_end + ", "; + log_array[i]['string'] += log_array[i].status; + } + return log_array; +} + +function endify(log_array) { + var Logs = log_array; + for (var i = 0; i < Logs.length; i++) { + if (Logs[i].status != 'closed') { + if (i < (Logs.length - 1) && Logs[i].assignment_id == Logs[i + 1].assignment_id && Logs[i].session_id == Logs[i + 1].session_id) { + Logs[i]['dt_end'] = Logs[i + 1]['dt_start']; + } + } + if (Logs[i]['dt_end'] == undefined) { + Logs[i]['dt_end'] = Logs[i]['dt_start'] + 1000; + } + + } + return Logs; +} +// ------------------------------------------------------- +// Collect data for a given Job and make a plain Array of log objects +// ------------------------------------------------------- +requestify.get(firebase_target_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var job_logs = response.getBody(); + var units = job_logs.units + for (var unit_id in units) { + var assignments = units[unit_id]['assignments']; + for (var assignment_id in assignments) { + var sessions = assignments[assignment_id]['sessions']; + for (var session_id in sessions) { + var logs = sessions[session_id]['tab_visibilty']; + // console.log(logs); + for (var log_id in logs) { + var log_record = { + task_id: task_id, + unit_id: parseInt(unit_id), + assignment_id: assignment_id, + session_id: parseInt(session_id), + dt_start: parseInt((logs[log_id].status != "closed")?logs[log_id].dt:logs[log_id].dt+500), + status: logs[log_id].status + }; + LOGS.push(log_record); + } + } + } + } + LOGS.sort(dynamicSortMultiple("session_id","dt_start")); + //LOGS.sort(dynamicSort("assignment_id")); + //LOGS.sort(dynamicSort("unit_id")); + + LOGS = endify(LOGS); + LOGS = stringify(LOGS); + console.log(LOGS.length); + var all_text = ""; + for (var i = 0; i < LOGS.length; i++) { + all_text+=LOGS[i]['string'] + '\n'; + } + fs.appendFile(filename, all_text, function(err) { + if (err) + console.log(err); + }); + console.log(firebase_target_url); + //console.log(firebase_target_url); + + }); diff --git a/logger.js b/logger.js index 063abc2..79650e1 100644 --- a/logger.js +++ b/logger.js @@ -1,8 +1,194 @@ -var firebase_reference = new Firebase("https://crowdworker-logger.firebaseio.com/trials"); - -firebase_reference.push({ - pathname: document.location.pathname, - search: document.location.search, - hostname: document.location.hostname, - hash: document.location.hash -}); +var EDA_LOGGER = EDA_LOGGER || (function() { + var settings = { + session: Math.floor(Math.random() * 1000000) + 1, + activity_interval: 2000, + debug: true, + firebase: { + bucket: "crowdworker-logger" + } + } + // A log function which can be easily turned of using debug variable + var log = function(message) { + if (settings.debug) console.log("[EDA_LOGGER] LOG: " + message); + }; + function fullPath(el){ + var names = []; + while (el.parentNode){ + if (el.id){ + names.unshift('#'+el.id); + break; + }else{ + if (el==el.ownerDocument.documentElement) + names.unshift(el.tagName); + else{ + for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++); + names.unshift(el.tagName+":nth-child("+c+")"); + } + el=el.parentNode; + } + } + return names.join(" > "); + } + function isPageHidden() { + return document.hidden || document.msHidden || document.webkitHidden || document.mozHidden; + } + function getSelectedText() { + var text = ""; + if (typeof window.getSelection != "undefined") { + text = window.getSelection().toString(); + } else if (typeof document.selection != "undefined" && document.selection.type == "Text") { + text = document.selection.createRange().text; + } + return text; + } + var _args = { + key_name: "test_name", + key_value: "test_value" + }; // private + return { + init: function(Args) { + _args = Args; + _args['task_id'] = parseInt(document.getElementById("assignment-job-id").innerHTML); + _args['worker_id'] = parseInt(document.getElementById("assignment-worker-id").innerHTML); + // Include Firebase library + var logger = this; + logger.init_firebase(function() { + logger.init_events_capturing(); + logger.init_activity_capturing(); + }); + }, + init_firebase: function(callback) { + var firebase_script = document.createElement('script'); + firebase_script.src = "https://cdn.firebase.com/js/client/2.2.9/firebase.js"; + + log(firebase_script.src); + + document.getElementsByTagName('head')[0].appendChild(firebase_script); + var logger = this; + firebase_script.onload = function() { + // get the assignment code from the url + var assignment_code = document.location.pathname.substring(document.location.pathname.lastIndexOf("/"), document.location.pathname.length); + log(assignment_code); + // get the platform code from the url + var platform_code = document.location.hostname.replace(/\./g, ''); + // form the firebase endpoint url + var firebase_endpoint_url = "https://" + settings.firebase.bucket + ".firebaseio.com/" + platform_code + "/" + _args["task_id"] + "/units/" + _args['key_value'] + "/assignments" + assignment_code; + log(firebase_endpoint_url); + _args["firebase_assignment"] = new Firebase(firebase_endpoint_url); + /*_args["firebase_assignment"].update({ + key_name: _args.key_name, + key_value: _args.key_value, + unit_id: _args.key_value + });*/ + _args["firebase_logs"] = _args["firebase_assignment"].child('sessions/' + settings.session + "/tab_visibilty"); + _args["firebase_activity"] = _args["firebase_assignment"].child('sessions/' + settings.session + "/page_activity"); + _args["firebase_keys"] = _args["firebase_assignment"].child('sessions/' + settings.session + "/key_pressed"); + _args["firebase_clicks"] = _args["firebase_assignment"].child('sessions/' + settings.session + "/clicks"); + + callback(); + }; + }, + init_activity_capturing: function() { + var logger = this; + var activity_statuses = { + "keyboard": 0, + "mouse": 0, + "scroll": 0, + "scroll_top":0, + "text_selected":0 + }; + setInterval(function() { + logger.log_event(_args["firebase_activity"], (function(a) { + activity_statuses = { + "keyboard": 0, + "mouse": 0, + "scroll": 0, + "scroll_top":0, + "text_selected":0 + }; + return a; + }(activity_statuses))); + }, settings.activity_interval); + + document.onkeydown = function(evt) { + activity_statuses["keyboard"] = 1; + }; + document.onmouseup = function(evt){ + if (getSelectedText() != "") + activity_statuses["text_selected"] = 1; + } + document.onmousemove = function(evt) { + activity_statuses["mouse"] = 1; + }; + + window.onscroll = function(evt) { + activity_statuses["scroll"] = 1; + activity_statuses["scroll_top"] = document.body.scrollTop; + }; + + document.onkeydown = function(evt) { + var key = evt.keyCode || evt.charCode; + logger.log_event(_args["firebase_keys"],{"key":key}); + }; + document.onmousedown = function(evt) { + var element_path = fullPath(evt.path[0]); + logger.log_event(_args["firebase_clicks"],{"element":element_path}); + }; + }, + init_events_capturing: function() { + var logger = this; + // Log the task was opened by the worker + logger.log_event(_args["firebase_logs"], { + status: "opened" + }); + // Log the task page was closed by the worker + window.onbeforeunload = function() { + logger.log_event(_args["firebase_logs"], { + status: "closed" + }); + }; + logger.init_visibility_changes(); + }, + init_visibility_changes: function() { + var hidden, visibilityChange; + if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support + hidden = "hidden"; + visibilityChange = "visibilitychange"; + } else if (typeof document.mozHidden !== "undefined") { + hidden = "mozHidden"; + visibilityChange = "mozvisibilitychange"; + } else if (typeof document.msHidden !== "undefined") { + hidden = "msHidden"; + visibilityChange = "msvisibilitychange"; + } else if (typeof document.webkitHidden !== "undefined") { + hidden = "webkitHidden"; + visibilityChange = "webkitvisibilitychange"; + } + var logger = this; + + function handleVisibilityChange() { + if (document[hidden]) { + logger.log_event(_args["firebase_logs"], { + status: "hidden" + }); + } else { + logger.log_event(_args["firebase_logs"], { + status: "active" + }); + } + } + // Warn if the browser doesn't support addEventListener or the Page Visibility API + if (typeof document.addEventListener === "undefined" || + typeof document[hidden] === "undefined") { + //alert("This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API."); + } else { + // Handle page visibility change + document.addEventListener(visibilityChange, handleVisibilityChange, false); + } + }, + log_event: function(firebase_reference, data) { + data['dt'] = Firebase.ServerValue.TIMESTAMP; + firebase_reference.push(data); + } + }; +}()); diff --git a/package.json b/package.json new file mode 100644 index 0000000..8579f97 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "Relauncher-Logger-ReferenceMaker", + "version": "0.0.1", + "description": "", + "main": "unit_references_maker.js", + "engines": { + "node": "0.10.29" + }, + "scripts": { + "start": "node unit_references_maker.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/ReLauncher/worker-activity-logger.git" + }, + "dependencies": { + "firebase":"2.3.1", + "requestify":"0.1.17" + } +} \ No newline at end of file diff --git a/procfile b/procfile new file mode 100644 index 0000000..104c7e4 --- /dev/null +++ b/procfile @@ -0,0 +1 @@ +web: node unit_references_maker.js \ No newline at end of file diff --git a/queue_management.js b/queue_management.js new file mode 100644 index 0000000..d120d72 --- /dev/null +++ b/queue_management.js @@ -0,0 +1,147 @@ +var Firebase = require('firebase'); +var requestify = require('requestify'); + + +var JOB_TO_BE_PROCESSED = 850870; +var DELAY_QUEUE_TIME = 30000; + + +var Crowdflower = { + base: "http://api.crowdflower.com/v1/", + api_key: process.env.CROWDFLOWER_API_KEY, + getUnit: function(job_id, unit_id, callback) { + var cf = Crowdflower; + var crowdflower_unit_url = cf.base + "/jobs/" + job_id + "/units/" + unit_id + ".json?key=" + cf.api_key; + console.log(crowdflower_unit_url); + requestify.get(crowdflower_unit_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var crowdflower_unit = response.getBody(); + callback(crowdflower_unit); + }); + } +} +var Firebase_urls = { + base: 'https://crowdworker-logger.firebaseio.com/', + crowdflower: "taskscrowdflowercom/", + queue: function() { + var fb = Firebase_urls; + return fb.base + "queue/"; + }, + tasks: function() { + var fb = Firebase_urls; + var url = fb.base + fb.crowdflower; + return url; + }, + assignments: function(job_id) { + var fb = Firebase_urls; + var url = fb.base + fb.crowdflower + job_id + "/"; + return url; + } +} + +function getLastLog(logs) { + if (logs) { + var last_log_id = Object.keys(logs)[Object.keys(logs).length - 1]; + var last_log = logs[last_log_id]; + return last_log; + } + return false; +} + + +function addToQueue(job_id, unit_id, assignment_id) { + console.log(DELAY_QUEUE_TIME / 1000 + " seconds passed"); + var assignment_ref = new Firebase(Firebase_urls.queue()); + assignment_ref.push({ + suspect: "abandoned", + job_id: job_id, + unit_id: unit_id, + assignment_id: assignment_id, + dt: Firebase.ServerValue.TIMESTAMP + }); + console.log("event is added to the queue"); +} + +function relaunchUnit(job_id, unit_id) { + requestify.post("http://localhost:3000/jobs/" + job_id + "/units/" + unit_id + "/relaunch", { + api_key: Crowdflower.api_key + }, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var resp = response.getBody(); + console.log(resp); + }); +} + +console.log("====================\nStart Logs Monitoring\n===================="); + +var logs_ref = new Firebase(Firebase_urls.assignments(JOB_TO_BE_PROCESSED)); +logs_ref.on('child_changed', function(assignment_snapshot, prevchildname) { + var job_id = JOB_TO_BE_PROCESSED; + + var assignment_details = assignment_snapshot.val(); + var assignment_id = assignment_snapshot.key(); + + var unit_id = assignment_details['unit_id']; + var logs = assignment_details['logs']; + + if (unit_id) { + last_log = getLastLog(logs); + if (last_log && last_log.status == "closed") { + console.log(unit_id, assignment_id, assignment_details["status"]); + //add the unit to queue to be processed + setTimeout(function() { + addToQueue(job_id, unit_id, assignment_id); + }, DELAY_QUEUE_TIME); + } + } +}); + + + + + +console.log("====================\nStart Queue Monitoring\n===================="); +var ref_queue = new Firebase(Firebase_urls.queue()); +ref_queue.on('child_added', function(queue_element_snapshot, prevchildname) { + console.log("processing a queue element"); + + var element = queue_element_snapshot.val(); + var unit_id = element['unit_id']; + var job_id = element['job_id']; + var assignment_id = element['assignment_id']; + var assignment_ref = new Firebase(Firebase_urls.assignments(job_id) + assignment_id + '/logs/'); + + assignment_ref.orderByChild("dt").limitToLast(1).once("value", function(log_snapshot) { + var logs = log_snapshot.val(); + var last_log = getLastLog(logs); + console.log(last_log); + // var logs = assignment['logs']; + // getLastLog(logs, function(last_log) { + if (last_log.status == "closed") { + console.log("check unit state at CrowdFlower"); + setTimeout(function() { + console.log(DELAY_QUEUE_TIME/1000+" seconds passed"); + Crowdflower.getUnit(job_id, unit_id, function(cf_unit) { + console.log(cf_unit['state']); + if (cf_unit['state'] == "judging") { + console.log("to relaunch the unit"); + relaunchUnit(job_id, unit_id); + } + }); + }, DELAY_QUEUE_TIME); + + } + }); + // remove element from queue, because it is checked; + var queue_element_to_be_deleted = new Firebase(Firebase_urls.queue() + queue_element_snapshot.key()); + queue_element_to_be_deleted.set(null); + console.log("element " + queue_element_snapshot.key() + " is removed"); +}); diff --git a/test.js b/test.js new file mode 100644 index 0000000..f860259 --- /dev/null +++ b/test.js @@ -0,0 +1,3 @@ +process.argv.forEach(function (val, index, array) { + console.log(index + ': ' + val); +}); \ No newline at end of file diff --git a/unit_ids_maker.js b/unit_ids_maker.js new file mode 100644 index 0000000..648eecd --- /dev/null +++ b/unit_ids_maker.js @@ -0,0 +1,54 @@ +var Firebase = require('firebase'); +var requestify = require('requestify'); +//var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +//var Ref = new Firebase(firebase_base_url); +var CROWDFLOWER_API_KEY = process.env.CROWDFLOWER_API_KEY; +var JOB_TO_BE_PROCESSED = 855249; + +var Crowdflower = { + base: "http://api.crowdflower.com/v1/", + api_key: process.env.CROWDFLOWER_API_KEY, + getUnits: function(job_id, callback) { + var cf = Crowdflower; + var crowdflower_units_url = cf.base + "/jobs/" + job_id + "/units.json?key=" + cf.api_key; + console.log(crowdflower_units_url); + requestify.get(crowdflower_units_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var units = response.getBody(); + if (callback) + callback(units); + }); + }, + updateUnit: function(job_id, unit_id, data, callback) { + var cf = Crowdflower; + var crowdflower_unit_url = cf.base + "/jobs/" + job_id + "/units/" + unit_id + ".json?key=" + cf.api_key; + + var put_data = { + unit: { + data: data + } + }; + console.log(put_data); + requestify.put(crowdflower_unit_url, put_data) + .then(function(crowdflower_resp) { + var unit_info = crowdflower_resp.getBody(); + if (callback) + callback(unit_info); + }); + } +} + +Crowdflower.getUnits(JOB_TO_BE_PROCESSED, function(units) { + for (var unit_id in units) { + var unit_data = units[unit_id]; + unit_data['re_unit_id'] = unit_id; + //console.log(unit_data); + Crowdflower.updateUnit(JOB_TO_BE_PROCESSED, unit_id, unit_data, function(updated_unit) { + console.log(updated_unit); + }); + } +}) diff --git a/unit_references_maker.js b/unit_references_maker.js new file mode 100644 index 0000000..6802f95 --- /dev/null +++ b/unit_references_maker.js @@ -0,0 +1,45 @@ +var Firebase = require('firebase'); +var requestify = require('requestify'); +var firebase_base_url = 'https://crowdworker-logger.firebaseio.com/' + "taskscrowdflowercom" + '/'; +var Ref = new Firebase(firebase_base_url); +var CROWDFLOWER_API_KEY = process.env.CROWDFLOWER_API_KEY; + +Ref.on('child_changed', function(task_snapshot, prevchildname) { + var task_id = task_snapshot.key(); + console.log(task_id); + var task_reference = Ref.child(task_id); + + task_reference.on('child_added', function(assignment_snapshot, prevchildname) { + var assignment_details = assignment_snapshot.val(); + var unit_id = assignment_details['unit_id']; + if (unit_id == undefined) { + var key_name = assignment_details['key_name']; + var unit_key = { + 'name': assignment_details['key_name'], + 'value': assignment_details['key_value'] + }; + console.log(unit_key); + var units_url = "http://api.crowdflower.com/v1/jobs/" + task_id + "/units/?key=" + CROWDFLOWER_API_KEY; + console.log(units_url); + + requestify.get(units_url, { + headers: { + "Accept": "application/json" + } + }) + .then(function(response) { + var units = response.getBody(); + //console.log(units); + for (var key in units) { + if (units[key][unit_key["name"]] == unit_key["value"]) { + task_reference.child(assignment_snapshot.key()).update({ + unit_id: key + }); + } + // do something with key + } + }); + + } + }); +});