-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[major changes] data transformed on the server
* Server now stores all the data and sends only the data necessary to view. Should allow for a much more responsive client. * Communication now through sockets in preparation for eventual move to electron * viz completely broken * sidebar UI * config can be changed through the GUI (buggy)
- Loading branch information
1 parent
14933e6
commit efff550
Showing
28 changed files
with
7,590 additions
and
4,048 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
{ | ||
"extends": "react-app" | ||
"extends": "react-app", | ||
"plugins": ["import"], | ||
"rules": { | ||
"import/no-unresolved": 2 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,22 @@ | ||
const server = require("./server/server"); | ||
const { parser } = require("./server/args"); | ||
const { parseConfig } = require("./server/config"); | ||
const Deque = require("collections/deque"); | ||
const { mapper } = require("./server/mapper"); | ||
const { demuxer } = require("./server/demuxer"); | ||
const getInitialConfig = require("./server/config").getInitialConfig; | ||
const { startUp } = require("./server/startUp"); | ||
const { startGuppyWatcher } = require("./server/guppyWatcher"); | ||
const { sleep } = require("./server/utils"); | ||
|
||
/* make some globals available everywhere */ | ||
global.args = parser.parseArgs(); | ||
global.config = parseConfig(global.args); | ||
const args = parser.parseArgs(); | ||
if (args.verbose) global.VERBOSE = true; | ||
if (args.mockFailures) global.MOCK_FAILURES = true; | ||
global.io = undefined; | ||
global.config = getInitialConfig(args) | ||
global.datastore = {}; | ||
global.barcodesSeen = new Set(); | ||
global.haveBeenSeen = new Set(); | ||
global.demuxQueue = new Deque(); | ||
global.mappingQueue = new Deque(); | ||
global.mappingResults = new Deque(); | ||
global.timeMap = new Map(); | ||
global.epochMap = new Map(); | ||
|
||
|
||
const startWatchers = () => { | ||
/* as things get pushed onto the deques, we want to spawn the | ||
appropriate processes (e.g. guppy, porechop). | ||
As things are processed, they are shifted off one deque and pushed | ||
onto another! */ | ||
global.demuxQueue.addRangeChangeListener(() => demuxer()); | ||
global.mappingQueue.addRangeChangeListener(() => mapper()); | ||
|
||
// start watchers | ||
demuxer(); | ||
mapper(); | ||
startGuppyWatcher(); | ||
} | ||
|
||
|
||
const main = async () => { | ||
await startUp(); /* block until we've read the appropriate files */ | ||
/* Listen on localhost and process requests from the client */ | ||
const app = server.run({}); // eslint-disable-line | ||
await sleep(200); | ||
startWatchers(); | ||
await startUp({emptyDemuxed: args.emptyDemuxed}); /* block until we've read the appropriate files */ | ||
const app = await server.run({devClient: args.devClient}); // eslint-disable-line | ||
} | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,71 +1,128 @@ | ||
const fs = require('fs') | ||
const path = require('path') | ||
const chalk = require('chalk'); | ||
const { getAbsolutePath } = require("./utils"); | ||
const { getAbsolutePath, verbose, log, warn } = require("./utils"); | ||
const { mapper } = require("./mapper"); | ||
|
||
const ensurePathExists = (p, {make=false}={}) => { | ||
if (!fs.existsSync(p)) { | ||
if (make) { | ||
console.log(chalk.yellowBright("Path", p, "created")); | ||
log(`Creating path ${p}`); | ||
fs.mkdirSync(p, {recursive: true}) | ||
} else { | ||
console.log("ERROR. Path", p, "doesn't exist."); | ||
process.exit(1); | ||
throw new Error(`ERROR. Path ${p} doesn't exist.`); | ||
} | ||
} | ||
} | ||
|
||
const parseConfig = (args) => { | ||
const configDir = path.dirname(getAbsolutePath(args.config)); | ||
let config = JSON.parse(fs.readFileSync(getAbsolutePath(args.config))); | ||
const getReferenceNames = (referencePanelPath) => { | ||
return fs.readFileSync(referencePanelPath, "utf8") | ||
.split("\n") | ||
.filter((l) => l.startsWith(">")) | ||
.map((n) => { | ||
if (n.indexOf(" ") > 0) { | ||
return { | ||
"name": n.substring(1, n.indexOf(" ")), // fasta name is up until the first space | ||
"description": n.substring(n.indexOf(" ")) // fasta description is the rest | ||
}; | ||
} else { | ||
return { | ||
"name": n.substring(1), | ||
"description": "" | ||
}; | ||
} | ||
}); | ||
} | ||
|
||
/* check config file has the appropriate fields... */ | ||
/** | ||
* Create initial config file from command line arguments | ||
*/ | ||
const getInitialConfig = (args) => { | ||
const barcodes = [ | ||
"BC01", "BC02", "BC03", "BC04", "BC05", "BC06", "BC07", "BC08", "BC09", "BC10", "BC11", "BC12" | ||
]; | ||
|
||
/* sort out paths */ | ||
config.referenceConfigPath = getAbsolutePath(config.referenceConfigPath, {relativeTo: configDir}); | ||
config.referencePanelPath = getAbsolutePath(config.referencePanelPath, {relativeTo: configDir}); | ||
const config = { | ||
title: args.title ? args.title : "", | ||
barcodeToName: {}, | ||
barcodes, | ||
basecalledPath: "", | ||
demuxedPath: "", | ||
referenceConfigPath: "", | ||
referencePanelPath: "", | ||
referencePanel: [], | ||
reference: undefined, | ||
relaxedDemuxing: args.relaxedDemuxing, | ||
}; | ||
|
||
if (args.basecalledDir) { | ||
config.basecalledPath = getAbsolutePath(args.basecalledDir, {relativeTo: process.cwd()}); | ||
console.log("BC:", config.basecalledPath) | ||
} else { | ||
config.basecalledPath = getAbsolutePath(config.basecalledPath, {relativeTo: configDir}); | ||
} | ||
config.demuxedPath = getAbsolutePath(config.demuxedPath, {relativeTo: configDir}); | ||
/* most options _can_ be specified on the command line, but may also be specified in the client */ | ||
barcodes.forEach((bc) => { | ||
config.barcodeToName[bc] = undefined; | ||
}) | ||
if (args.barcodeNames) { | ||
args.barcodeNames.forEach((raw) => { | ||
const [bc, name] = raw.split('='); | ||
if (!barcodes.includes(bc)) { | ||
throw new Error(`Invalid barcode ${bc}`) | ||
} | ||
config.barcodeToName[bc] = name; | ||
}); | ||
} | ||
|
||
/* check if paths exist (perhaps we could make them if they don't) */ | ||
ensurePathExists(config.referenceConfigPath); | ||
ensurePathExists(config.referencePanelPath); | ||
if (args.basecalledDir !== "") { | ||
config.basecalledPath = getAbsolutePath(args.basecalledDir, {relativeTo: process.cwd()}); | ||
} | ||
if (args.demuxedDir !== "") { | ||
config.demuxedPath = getAbsolutePath(args.demuxedDir, {relativeTo: process.cwd()}); | ||
ensurePathExists(config.demuxedPath, {make: true}); | ||
} | ||
|
||
if (args.referencePanelPath) { | ||
ensurePathExists(args.referencePanelPath); | ||
config.referencePanelPath = getAbsolutePath(args.referencePanelPath, {relativeTo: process.cwd()}); | ||
config.referencePanel = getReferenceNames(config.referencePanelPath); | ||
} | ||
|
||
if (args.referenceConfigPath) { | ||
ensurePathExists(args.referenceConfigPath); | ||
config.referenceConfigPath = getAbsolutePath(args.referenceConfigPath, {relativeTo: process.cwd()}); | ||
|
||
/* parse the "main reference" configuration file (e.g. primers, genes, ref seq etc) */ | ||
const secondConfig = JSON.parse(fs.readFileSync(config.referenceConfigPath)); | ||
config = {...config, ...secondConfig}; | ||
|
||
/* get the names of the sequences in the reference panel */ | ||
config.referencePanel = fs.readFileSync(config.referencePanelPath, "utf8") | ||
.split("\n") | ||
.filter((l) => l.startsWith(">")) | ||
.map((n) => { | ||
if (n.indexOf(" ") > 0) { | ||
return { | ||
"name": n.substring(1, n.indexOf(" ")), // fasta name is up until the first space | ||
"description": n.substring(n.indexOf(" ")) // fasta description is the rest | ||
}; | ||
} else { | ||
return { | ||
"name": n.substring(1), | ||
"description": "" | ||
}; | ||
} | ||
}); // remove the > character | ||
|
||
/* things that may be put into the config JSON in the future */ | ||
config.maxMappingFilesPerRequest = 100; | ||
|
||
return config; | ||
const reference = JSON.parse(fs.readFileSync(config.referenceConfigPath)).reference; | ||
config.reference = reference; | ||
|
||
} | ||
|
||
|
||
return config; | ||
}; | ||
|
||
/** | ||
* update the config file via GUI provided data | ||
*/ | ||
const modifyConfig = (newConfig) => { | ||
|
||
global.config.barcodeToName = newConfig.barcodeToName; | ||
|
||
if (!global.config.referencePanelPath && newConfig.referencePanelPath) { | ||
try { | ||
ensurePathExists(newConfig.referencePanelPath); | ||
// take approprieate action? | ||
} catch (err) { | ||
warn(err.message); | ||
newConfig.referencePanelPath = ""; | ||
} | ||
} | ||
|
||
global.config = Object.assign({}, global.config, newConfig); | ||
|
||
/* try to start the mapper, which may not be running due to insufficent | ||
config information. It will exit gracefully if required */ | ||
mapper(); | ||
|
||
} | ||
|
||
|
||
module.exports = { | ||
parseConfig | ||
getInitialConfig, | ||
modifyConfig | ||
}; |
Oops, something went wrong.