@@ -18,23 +18,35 @@ import open = require('open');
18
18
import * as _ from 'lodash' ;
19
19
20
20
import * as csv from './csv' ;
21
+ import { makeTemplate } from './sample-template' ;
21
22
import * as utils from './utils' ;
22
23
import { outputFile } from 'fs-extra' ;
23
24
24
25
program
25
26
. version ( '2.0.0' )
26
27
. usage ( '[options] template.html tasks.csv outputs.csv' )
27
28
. 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.' )
28
31
. option ( '-w, --write-template' , 'Generate a stub template file based on the input CSV.' )
29
32
. parse ( process . argv ) ;
30
33
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 ) ) ) {
33
37
program . help ( ) ;
34
38
}
39
+ if ( writeTemplate ) {
40
+ // tasks.csv is the only input with --write-template.
41
+ args . unshift ( '' ) ;
42
+ args . push ( '' ) ;
43
+ }
35
44
36
45
const [ templateFile , tasksFile , outputsFile ] = args ;
37
46
const 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 ) ;
38
50
39
51
type Task = { [ key : string ] : string } ;
40
52
let flash = '' ; // this is used to show warnings in the web UI.
@@ -45,6 +57,7 @@ async function renderTemplate({task, numCompleted, numTotal}: TaskStats) {
45
57
for ( const k in task ) {
46
58
fullDict [ k ] = utils . htmlEntities ( task [ k ] ) ;
47
59
}
60
+ // Note: these two fields are not available in mechanical turk.
48
61
fullDict [ 'ALL_JSON' ] = utils . htmlEntities ( JSON . stringify ( task , null , 2 ) ) ;
49
62
fullDict [ 'ALL_JSON_RAW' ] = JSON . stringify ( task ) ;
50
63
const userHtml = utils . renderTemplate ( template , fullDict ) ;
@@ -56,19 +69,31 @@ async function renderTemplate({task, numCompleted, numTotal}: TaskStats) {
56
69
`<input type=hidden name="${ k } " value="${ utils . htmlEntities ( v ) } ">`
57
70
) . join ( '\n' ) ;
58
71
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
+ ` ;
72
97
}
73
98
74
99
async function readCompletedTasks ( ) : Promise < Task [ ] > {
@@ -119,15 +144,10 @@ async function getNextTask(): Promise<TaskStats> {
119
144
}
120
145
}
121
146
122
- if ( program [ 'write-template' ] ) {
123
- // TODO(danvk): implement.
124
- process. exit ( 0 ) ;
125
- }
126
-
127
147
const app = express ( ) ;
128
148
app . use ( bodyParser . urlencoded ( { extended : false } ) ) ;
129
149
app . use ( errorhandler ( ) ) ;
130
- app . use ( express . static ( path . resolve ( path . dirname ( templateFile ) ) ) ) ;
150
+ app . use ( express . static ( path . resolve ( staticDir ) ) ) ;
131
151
132
152
app . get ( '/' , utils . wrapPromise ( async ( req , res ) => {
133
153
const nextTask = await getNextTask ( ) ;
@@ -155,7 +175,17 @@ app.post('/delete-last', utils.wrapPromise(async (req, res) => {
155
175
res . redirect ( '/' ) ;
156
176
} ) ) ;
157
177
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