@@ -18,23 +18,35 @@ import open = require('open');
1818import * as _ from 'lodash' ;
1919
2020import * as csv from './csv' ;
21+ import { makeTemplate } from './sample-template' ;
2122import * as utils from './utils' ;
2223import { outputFile } from 'fs-extra' ;
2324
2425program
2526 . version ( '2.0.0' )
2627 . usage ( '[options] template.html tasks.csv outputs.csv' )
2728 . option ( '-p, --port <n>' , 'Run on this port (default 4321)' , parseInt )
29+ . option ( '-s, --static-dir <dir>' ,
30+ 'Serve static content from this directory. Default is same directory as template file.' )
2831 . option ( '-w, --write-template' , 'Generate a stub template file based on the input CSV.' )
2932 . parse ( process . argv ) ;
3033
31- const { args} = program ;
32- if ( 3 !== args . length ) {
34+ const { args, writeTemplate} = program ;
35+ if ( ! ( ( 3 === args . length && ! writeTemplate ) ||
36+ ( 1 === args . length && writeTemplate ) ) ) {
3337 program . help ( ) ;
3438}
39+ if ( writeTemplate ) {
40+ // tasks.csv is the only input with --write-template.
41+ args . unshift ( '' ) ;
42+ args . push ( '' ) ;
43+ }
3544
3645const [ templateFile , tasksFile , outputsFile ] = args ;
3746const port = program . port || 4321 ;
47+ // --static-dir is particularly useful for classify-images, where the template file is in a
48+ // temporary directory but the image files could be anywhere.
49+ const staticDir = program [ 'staticDir' ] || path . dirname ( templateFile ) ;
3850
3951type Task = { [ key : string ] : string } ;
4052let flash = '' ; // this is used to show warnings in the web UI.
@@ -45,6 +57,7 @@ async function renderTemplate({task, numCompleted, numTotal}: TaskStats) {
4557 for ( const k in task ) {
4658 fullDict [ k ] = utils . htmlEntities ( task [ k ] ) ;
4759 }
60+ // Note: these two fields are not available in mechanical turk.
4861 fullDict [ 'ALL_JSON' ] = utils . htmlEntities ( JSON . stringify ( task , null , 2 ) ) ;
4962 fullDict [ 'ALL_JSON_RAW' ] = JSON . stringify ( task ) ;
5063 const userHtml = utils . renderTemplate ( template , fullDict ) ;
@@ -56,19 +69,31 @@ async function renderTemplate({task, numCompleted, numTotal}: TaskStats) {
5669 `<input type=hidden name="${ k } " value="${ utils . htmlEntities ( v ) } ">`
5770 ) . join ( '\n' ) ;
5871
59- return `
60- <!doctype html>
61- <html>
62- <title>${ numCompleted } / ${ numTotal } - localturk</title>
63- <body><form action=/submit method=post>
64- <p>${ numCompleted } / ${ numTotal } <span style="background: yellow">${ thisFlash } </span></p>
65- ${ sourceInputs }
66- ${ userHtml }
67- <hr/><input type=submit />
68- </form>
69- </body>
70- </html>
71- ` ;
72+ return utils . dedent `
73+ <!doctype html>
74+ <html>
75+ <title>${ numCompleted } / ${ numTotal } - localturk</title>
76+ <body><form action=/submit method=post>
77+ <p>${ numCompleted } / ${ numTotal } <span style="background: yellow">${ thisFlash } </span></p>
78+ ${ sourceInputs }
79+ ${ userHtml }
80+ <hr/><input type=submit />
81+ </form>
82+ <script>
83+ // Support keyboard shortcuts via, e.g. <.. data-key="1" />
84+ window.addEventListener("keydown", function(e) {
85+ if (document.activeElement !== document.body) return;
86+ var key = e.key;
87+ const el = document.querySelector('[data-key="' + key + '"]');
88+ if (el) {
89+ e.preventDefault();
90+ el.click();
91+ }
92+ });
93+ </script>
94+ </body>
95+ </html>
96+ ` ;
7297}
7398
7499async function readCompletedTasks ( ) : Promise < Task [ ] > {
@@ -119,15 +144,10 @@ async function getNextTask(): Promise<TaskStats> {
119144 }
120145}
121146
122- if ( program [ 'write-template' ] ) {
123- // TODO(danvk): implement.
124- process. exit ( 0 ) ;
125- }
126-
127147const app = express ( ) ;
128148app . use ( bodyParser . urlencoded ( { extended : false } ) ) ;
129149app . use ( errorhandler ( ) ) ;
130- app . use ( express . static ( path . resolve ( path . dirname ( templateFile ) ) ) ) ;
150+ app . use ( express . static ( path . resolve ( staticDir ) ) ) ;
131151
132152app . get ( '/' , utils . wrapPromise ( async ( req , res ) => {
133153 const nextTask = await getNextTask ( ) ;
@@ -155,7 +175,17 @@ app.post('/delete-last', utils.wrapPromise(async (req, res) => {
155175 res . redirect ( '/' ) ;
156176} ) ) ;
157177
158- app . listen ( port ) ;
159- const url = `http :/ / localhost :${port } `;
160- console . log ( 'Running local turk on' , url ) ;
161- open ( url ) ;
178+
179+ if ( writeTemplate ) {
180+ ( async ( ) => {
181+ const columns = await csv . readHeaders ( tasksFile ) ;
182+ console . log ( makeTemplate ( columns ) ) ;
183+ } ) ( ) . catch ( e => {
184+ console . error ( e ) ;
185+ } ) ;
186+ } else {
187+ app . listen ( port ) ;
188+ const url = `http :/ / localhost :${port } `;
189+ console . log ( 'Running local turk on' , url ) ;
190+ open ( url ) ;
191+ }
0 commit comments