Skip to content

Improve runner app #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions bin/plotly-graph-exporter_electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,26 @@ if (!fs.existsSync(argv.outputDir)) {

getStdin().then((txt) => {
const hasStdin = !!txt
const pipeToStdOut = hasStdin && !argv.output && !argv.outputDir
const pipeToStdOut = hasStdin && !argv.output
const showLogs = !pipeToStdOut && (DEBUG || argv.verbose)
const input = hasStdin ? argv._.concat([txt]) : argv._
const getItemName = makeGetItemName(input)

const write = (info, _, done) => {
const itemName = getItemName(info)
const outPath = path.resolve(argv.outputDir, `${itemName}.${info.format}`)

if (pipeToStdOut) {
str(info.body)
.pipe(process.stdout.on('drain', done))
} else {
fs.writeFile(outPath, info.body, done)
}
}

const app = plotlyExporter.run({
input: input,
write: write,
debug: DEBUG,
parallelLimit: argv.parallelLimit,
component: {
Expand All @@ -56,19 +69,10 @@ getStdin().then((txt) => {

app.on('after-export', (info) => {
const itemName = getItemName(info)
const outPath = path.resolve(argv.outputDir, `${itemName}.${info.format}`)

if (showLogs) {
console.log(`exported ${itemName}, in ${info.processingTime} ms`)
}

if (pipeToStdOut) {
str(info.body).pipe(process.stdout)
} else {
fs.writeFile(outPath, info.body, (err) => {
if (err) console.warn(err)
})
}
})

app.on('export-error', (info) => {
Expand All @@ -90,7 +94,7 @@ getStdin().then((txt) => {

const msg = `\ndone with code ${info.code} in ${timeStr} - ${info.msg}`

if (info.code === 200) {
if (info.code === 0) {
if (showLogs) {
console.log(msg)
}
Expand Down
4 changes: 4 additions & 0 deletions src/app/runner/coerce-opts.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ function coerceOpts (_opts = {}) {
throw new Error('no valid input given')
}

opts.write = typeof _opts.write === 'function'
? _opts.write
: false

opts.input = input

return opts
Expand Down
5 changes: 3 additions & 2 deletions src/app/runner/constants.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module.exports = {
statusMsg: {
200: 'all task(s) completed',
0: 'all task(s) completed',
1: 'failed or incomplete task(s)',
422: 'json parse error',
500: 'incomplete task(s)'
501: 'failed export'
},
dflt: {
parallelLimit: 4
Expand Down
44 changes: 35 additions & 9 deletions src/app/runner/get-body.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const fs = require('fs')
const isUrl = require('is-url')
const isPlainObj = require('is-plain-obj')
const request = require('request')

/**
Expand All @@ -9,19 +10,44 @@ const request = require('request')
* - body
*/
function getBody (item, cb) {
if (fs.existsSync(item)) {
fs.readFile(item, 'utf-8', cb)
} else if (fs.existsSync(item + '.json')) {
fs.readFile(item + '.json', 'utf-8', cb)
} else if (isUrl(item)) {
request.get(item, (err, res, body) => {
let p
let done

// if item is object and has 'figure' key,
// only parse its 'figure' value and accumulate it with item
// to form body object
if (isPlainObj(item) && item.figure) {
p = item.figure
done = (err, _figure) => {
let figure

try {
figure = JSON.parse(_figure)
} catch (e) {
return cb(e)
}

const body = Object.assign({}, item, {figure: figure})
cb(err, body)
}
} else {
p = item
done = cb
}

if (fs.existsSync(p)) {
fs.readFile(p, 'utf-8', done)
} else if (fs.existsSync(p + '.json')) {
fs.readFile(p + '.json', 'utf-8', done)
} else if (isUrl(p)) {
request.get(p, (err, res, body) => {
if (err) {
return cb(err)
return done(err)
}
cb(null, body)
done(null, body)
})
} else {
cb(null, item)
done(null, item)
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/app/runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function createApp (_opts) {

win.on('closed', () => {
win = null
index.destroy()
})

createIndex(opts.component, opts, (_index) => {
Expand All @@ -46,10 +47,6 @@ function createApp (_opts) {
})
})

process.on('exit', () => {
index.destroy()
})

return app
}

Expand Down
63 changes: 41 additions & 22 deletions src/app/runner/run.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const uuid = require('uuid/v4')
const isNumeric = require('fast-isnumeric')
const parallelLimit = require('run-parallel-limit')

const createTimer = require('../../util/create-timer')
Expand All @@ -25,8 +26,9 @@ function run (app, win, ipcMain, opts) {
const totalTimer = createTimer()

let pending = input.length
let failed = 0

const tasks = input.map((item, i) => (done) => {
const tasks = input.map((item, i) => (cb) => {
const timer = createTimer()
const id = uuid()

Expand All @@ -38,23 +40,38 @@ function run (app, win, ipcMain, opts) {
id: id
}

const errorOut = (code) => {
fullInfo.msg = fullInfo.msg || STATUS_MSG[code] || ''
// task callback wrapper:
// - emits 'export-error' if given error code or error obj/msg
// - emits 'after-export' if no argument is given
const done = (err) => {
fullInfo.pending = --pending
fullInfo.processingTime = timer.end()

if (err) {
failed++

app.emit('export-error', Object.assign(
{code: code},
fullInfo
))
if (isNumeric(err)) {
fullInfo.code = err
} else {
fullInfo.code = 501
fullInfo.error = err
}

fullInfo.msg = fullInfo.msg || STATUS_MSG[fullInfo.code] || ''
app.emit('export-error', fullInfo)
} else {
app.emit('after-export', fullInfo)
}

return done()
cb()
}

// setup parse callback
const sendToRenderer = (errorCode, parseInfo) => {
Object.assign(fullInfo, parseInfo)

if (errorCode) {
return errorOut(errorCode)
return done(errorCode)
}

win.webContents.send(comp.name, id, fullInfo, compOpts)
Expand All @@ -65,22 +82,22 @@ function run (app, win, ipcMain, opts) {
Object.assign(fullInfo, convertInfo)

if (errorCode) {
return errorOut(errorCode)
return done(errorCode)
}

fullInfo.pending = --pending
fullInfo.processingTime = timer.end()

app.emit('after-export', fullInfo)
done()
if (opts.write) {
opts.write(fullInfo, compOpts, done)
} else {
done()
}
}

// setup convert on render message -> emit 'after-export'
ipcMain.once(id, (event, errorCode, renderInfo) => {
Object.assign(fullInfo, renderInfo)

if (errorCode) {
return errorOut(errorCode)
return done(errorCode)
}

comp._module.convert(fullInfo, compOpts, reply)
Expand All @@ -91,14 +108,14 @@ function run (app, win, ipcMain, opts) {
let body

if (err) {
return errorOut(422)
return done(422)
}

if (typeof _body === 'string') {
try {
body = JSON.parse(_body)
} catch (e) {
return errorOut(422)
return done(422)
}
} else {
body = _body
Expand All @@ -109,16 +126,18 @@ function run (app, win, ipcMain, opts) {
})

parallelLimit(tasks, opts.parallelLimit, (err) => {
const code = (err || pending !== 0) ? 500 : 200
const exitCode = (err || pending > 0 || failed > 0) ? 1 : 0

app.emit('after-export-all', {
code: code,
msg: STATUS_MSG[code],
code: exitCode,
msg: STATUS_MSG[exitCode],
totalProcessingTime: totalTimer.end()
})

// do not close window to look for unlogged console errors
if (!opts.debug) app.quit()
if (!opts.debug) {
app.exit(exitCode)
}
})
}

Expand Down
2 changes: 1 addition & 1 deletion test/integration/plotly-graph-exporter_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ tap.test('should print export info on success', t => {

const matches = [
/^exported fig/,
/done with code 200/
/done with code 0/
]

let i = 0
Expand Down
Loading