Skip to content
Snippets Groups Projects
Commit 5f5ff502 authored by Lisa Fiedler's avatar Lisa Fiedler
Browse files

Web Application Access

DeGeCI can now also be used via a web frontend
parent c90b1a19
No related branches found
No related tags found
No related merge requests found
Showing
with 3100 additions and 1 deletion
#!/bin/bash
# Returns the absolute path of this script regardless of symlinks
abs_path() {
# From: http://stackoverflow.com/a/246128
# - To resolve finding the directory after symlinks
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
echo "$( cd -P "$( dirname "$SOURCE" )" && pwd )"
}
BIN=`abs_path`
WEB=${BIN}/web
cd ${WEB}
echo "Access the web application by pasting http://localhost:3000 into your browser"
echo "Log files can be accessed at /tmp/degeci.log"
npm run start 2>&1 > /tmp/degeci.log
...@@ -4,6 +4,7 @@ A tool for annotation of mitochondrial genomes ...@@ -4,6 +4,7 @@ A tool for annotation of mitochondrial genomes
## Prerequisites ## Prerequisites
- Java 8 (jre) - Java 8 (jre)
- Docker or postgresql server - Docker or postgresql server
- For web Application: npm >= 9.8.1
## Initialization and downloads ## Initialization and downloads
...@@ -33,7 +34,7 @@ Database MDBG with schemas *complete* with following tables: ...@@ -33,7 +34,7 @@ Database MDBG with schemas *complete* with following tables:
## Taxonomic filtering ## Taxonomic filtering
Optionally, only a subset of the species in the database fullfilling a certain taxonomic constraint can be used for the annotation. By providing the scientific names (--taxonomic-groups <group1> <group1>) or the taxid (--taxids <id1> <id2>) of one or several taxonomic groups, all database species in the phylogenetic subtree below the lowest common ancestor of the specified taxonomic groups are determined. Optionally, only a subset of the species in the database fullfilling a certain taxonomic constraint can be used for the annotation. By providing the scientific names (--taxonomic-groups <group1> <group1>) or the taxid (--taxids <id1> <id2>) of one or several taxonomic groups, all database species in the phylogenetic subtree below the lowest common ancestor of the specified taxonomic groups are determined.
By using --exclude-taxids these species will NOT be used for the annotation, otherwise ONLY these species will be used for the annotation. By using --exclude-taxids these species will NOT be used for the annotation, otherwise ONLY these species will be used for the annotation.
## Annotation ## Annotation
...@@ -74,6 +75,13 @@ One example for basic application from a sequence string. ...@@ -74,6 +75,13 @@ One example for basic application from a sequence string.
3. An faa file containing the subsequences of the detected genes 3. An faa file containing the subsequences of the detected genes
5. A parameter setting file 5. A parameter setting file
## Web application
To use the Web Frontend. Once the application is started, it can be accessed in the browser at: localhost:3000
**Execute:**
./DeGeCI_web
## Bugs ## Bugs
Should you encounter any bugs, please report to lfiedler@informatik.uni-leipzig.de Should you encounter any bugs, please report to lfiedler@informatik.uni-leipzig.de
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
const bodyParser = require("body-parser");
var indexRouter = require('./routes/index');
var catalogRouter = require('./routes/catalog');
// safety precautions
const compression = require("compression");
const helmet = require("helmet");
const RateLimit = require("express-rate-limit");
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(bodyParser.raw());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(compression())
// app.use(
// helmet.contentSecurityPolicy({
// directives: {
// defaultSrc: ["'self'","auto","autoExample","bootstrapJS", "https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js","https://cdn.jsdelivr.net/gh/lekoala/bootstrap5-autocomplete@master/autocomplete.js"],
// scriptSrc: ["'self'", "auto","autoExample","bootstrapJS", "https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js","https://cdn.jsdelivr.net/gh/lekoala/bootstrap5-autocomplete@master/autocomplete.js"],
// styleSrc: ["'self'", "bootstrapCSS","https://cdn.jsdelivr.net/npm/bootstrap-dark-5@1/dist/css/bootstrap-dark.min.css"],
// },
// }),
// );
app.use(
helmet({
contentSecurityPolicy: false,
})
);
const limiter = RateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
max: 20,
});
//Apply rate limiter to all requests
app.use(limiter);
app.use('/', indexRouter);
app.use('/catalog', catalogRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('degeci:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
const { body,validationResult , oneOf, check} = require("express-validator");
const { ExpectationFailed } = require("http-errors");
let result_controller = require('../controller/resultController');
let fs = require('promise-fs')
const AdmZip = require('adm-zip')
const { spawn,exec, spawnSync } = require('child_process');
const { v4: uuidv4 } = require('uuid');
const { resolve } = require("path");
const fsSync = require('fs-extra');
const totalComputeNodes = 1;
let freeComputeNodes = totalComputeNodes;
const pathes = require('../projectDirectories')
const downloadPath = pathes.downloadPath;
const javaDir = pathes.javaDir;
exports.enter_sequence_string_get = function(req,res,next) {
res.render('annotation_form', {title: 'Enter Query Sequence'});
};
let nucleotideRegex = /^[ACGTNKMBVSWDYRH]+$/;
let fastaHeaderRegex = /^>.*/;
function getFreeComputeNode(req,res) {
// all graphs are busy
return new Promise((resolve, reject) => {
console.log("Free compute nodes: " + freeComputeNodes)
if(freeComputeNodes > 0) {
freeComputeNodes = freeComputeNodes-1;
resolve(freeComputeNodes);
}
else {
let done = setInterval(() => {
if(freeComputeNodes > 0) {
freeComputeNodes = freeComputeNodes-1;
clearInterval(done);
resolve(freeComputeNodes);
}
// console.log("still waiting");
},10000);
}
});
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function clearAnnotationDirectory(dir, t){
return new Promise( (resolve, reject) => {
resolve(setTimeout(
() =>{
fsSync.remove(dir)
}
,
t))
})
}
function moveAnnotations(moveFrom, moveTo) {
const zip = new AdmZip();
const id = moveFrom.substring(moveFrom.lastIndexOf("/")+1);
zip.addLocalFolder(moveFrom);
zip.writeZip(moveFrom+"/degeciAnnotations.zip")
fsSync.moveSync(moveFrom,moveTo ,(err) => {
if (err) throw err;});
}
function launchAnnotation(req,res,paramArray,mail, id,resultDir){
// data available for 48h -> delete thereafter
const dataAvailabilityTime = 172800000
paramArray = [javaDir].concat(paramArray);
console.log("Free compute nodes after: " + freeComputeNodes);
const link = pathes.domain + "/catalog/download/" + id;
let workerProcess = spawn('bash',paramArray );
return new Promise((resolve, reject) =>{
workerProcess.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
workerProcess.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
workerProcess.on('close', function (code) {
console.log('child process exited with code ' + code);
if(code == 0){
// move the results to direcotry where they will be available for dataAvailibilityTime ms
moveAnnotations(resultDir,pathes.annotationPath + "/"+id)
// delete this directory after dataAvailibilityTime ms
clearAnnotationDirectory(pathes.annotationPath + "/"+id,dataAvailabilityTime)
console.log("updating " + freeComputeNodes)
freeComputeNodes= freeComputeNodes+1;
resolve(link);
}
else {
console.log("error!")
console.log("updating " + freeComputeNodes)
freeComputeNodes= freeComputeNodes+1;
reject(null);
}
});
});
}
function setUpResultDirectories(uid) {
let dir = downloadPath+ "/" + uid ;
fsSync.mkdir(dir, { recursive: true }, (err) => {
if (err) throw err;
});
return dir;
}
function extractSequenceFromFasta(fastaFile) {
const data = fsSync.readFileSync(fastaFile,'utf8');
lines = data.split("\n");
let sequence = "";
for(let lineCounter = 1; lineCounter < lines.length; lineCounter++) {
sequence += lines[lineCounter].replace("\n","");
}
return sequence;
}
exports.enter_sequence_string_post = async function(req,res,next, uid) {
try {
await getFreeComputeNode(req,res);
} catch (error) {
console.log(error);
}
console.log("Free compute nodes after: " + freeComputeNodes)
console.log("got free compute node");
console.log(req.body.composition)
console.log(req.body.refseq)
let jobid = req.body.id;
let dir = setUpResultDirectories(uid);
if(jobid == "") {
jobid = dir.substring(dir.lastIndexOf("/")+1);
}
console.log(dir)
let sequence = "";
// sequence was supplied as text
if(req.body.sequence != undefined) {
console.log("Sequence from text");
sequence = req.body.sequence.trim();}
else {
console.log("Sequence from fasta file");
sequence = extractSequenceFromFasta(req.file.path).trim();
console.log("Sequence size: "+ sequence.length);}
let taxids;
console.log(req.body.taxList)
if(req.body.taxList == "") {
console.log("empty")
taxids = [];
}
else {
taxids = req.body.taxList.trim().split(" ");
}
let paramArray = [
"--name",jobid,
"--result-directory",dir,
"--sequence", sequence ,
"--alpha", req.body.alpha]
if(!req.body.composition) {
console.log("linear")
paramArray.push("--treat-linear")
paramArray.push("true")
}
if(req.body.gencode != -1){
paramArray.push("--trans-table");
paramArray.push(req.body.gencode);
}
paramArray.push("--refseq");
if(req.body.refseq == "RefSeq89"){
paramArray.push(89);
}
else{
paramArray.push(204);
}
if(taxids.length > 0) {
paramArray.push("--exclude-taxids");
const val = req.body.optradio == "o1" ? "false" : "true";
paramArray.push(val);
paramArray.push("--taxonomic-groups");
paramArray = paramArray.concat(taxids);
}
console.log(paramArray);
let link =null;
try {
link = await launchAnnotation(req,res,paramArray,req.body.mail,dir.substring(dir.lastIndexOf("/")+1),dir);
console.log("after launch")
} catch (error) {
console.log(error);
}
return new Promise((resolve, reject) => {
if(link != null){
resolve(link)
}
else{
reject(link);
}
})
}
const { body,validationResult } = require("express-validator");
exports.show_results = function(req,res,next) {
res.render('result', {result: req.body.sequence});
};
\ No newline at end of file
../acorn/bin/acorn
\ No newline at end of file
../mime/cli.js
\ No newline at end of file
../mkdirp/bin/cmd.js
\ No newline at end of file
../nodemon/bin/nodemon.js
\ No newline at end of file
../touch/bin/nodetouch.js
\ No newline at end of file
../nopt/bin/nopt.js
\ No newline at end of file
../@babel/parser/bin/babel-parser.js
\ No newline at end of file
../resolve/bin/resolve
\ No newline at end of file
../semver/bin/semver.js
\ No newline at end of file
../uuid/dist/bin/uuid
\ No newline at end of file
This diff is collapsed.
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# @babel/helper-string-parser
> A utility package to parse strings
See our website [@babel/helper-string-parser](https://babeljs.io/docs/en/babel-helper-string-parser) for more information.
## Install
Using npm:
```sh
npm install --save @babel/helper-string-parser
```
or using yarn:
```sh
yarn add @babel/helper-string-parser
```
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.readCodePoint = readCodePoint;
exports.readInt = readInt;
exports.readStringContents = readStringContents;
var _isDigit = function isDigit(code) {
return code >= 48 && code <= 57;
};
const forbiddenNumericSeparatorSiblings = {
decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
hex: new Set([46, 88, 95, 120])
};
const isAllowedNumericSeparatorSibling = {
bin: ch => ch === 48 || ch === 49,
oct: ch => ch >= 48 && ch <= 55,
dec: ch => ch >= 48 && ch <= 57,
hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
};
function readStringContents(type, input, pos, lineStart, curLine, errors) {
const initialPos = pos;
const initialLineStart = lineStart;
const initialCurLine = curLine;
let out = "";
let firstInvalidLoc = null;
let chunkStart = pos;
const {
length
} = input;
for (;;) {
if (pos >= length) {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
out += input.slice(chunkStart, pos);
break;
}
const ch = input.charCodeAt(pos);
if (isStringEnd(type, ch, input, pos)) {
out += input.slice(chunkStart, pos);
break;
}
if (ch === 92) {
out += input.slice(chunkStart, pos);
const res = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors);
if (res.ch === null && !firstInvalidLoc) {
firstInvalidLoc = {
pos,
lineStart,
curLine
};
} else {
out += res.ch;
}
({
pos,
lineStart,
curLine
} = res);
chunkStart = pos;
} else if (ch === 8232 || ch === 8233) {
++pos;
++curLine;
lineStart = pos;
} else if (ch === 10 || ch === 13) {
if (type === "template") {
out += input.slice(chunkStart, pos) + "\n";
++pos;
if (ch === 13 && input.charCodeAt(pos) === 10) {
++pos;
}
++curLine;
chunkStart = lineStart = pos;
} else {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
}
} else {
++pos;
}
}
return {
pos,
str: out,
firstInvalidLoc,
lineStart,
curLine,
containsInvalid: !!firstInvalidLoc
};
}
function isStringEnd(type, ch, input, pos) {
if (type === "template") {
return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
}
return ch === (type === "double" ? 34 : 39);
}
function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
const throwOnInvalid = !inTemplate;
pos++;
const res = ch => ({
pos,
ch,
lineStart,
curLine
});
const ch = input.charCodeAt(pos++);
switch (ch) {
case 110:
return res("\n");
case 114:
return res("\r");
case 120:
{
let code;
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
return res(code === null ? null : String.fromCharCode(code));
}
case 117:
{
let code;
({
code,
pos
} = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
return res(code === null ? null : String.fromCodePoint(code));
}
case 116:
return res("\t");
case 98:
return res("\b");
case 118:
return res("\u000b");
case 102:
return res("\f");
case 13:
if (input.charCodeAt(pos) === 10) {
++pos;
}
case 10:
lineStart = pos;
++curLine;
case 8232:
case 8233:
return res("");
case 56:
case 57:
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(pos - 1, lineStart, curLine);
}
default:
if (ch >= 48 && ch <= 55) {
const startPos = pos - 1;
const match = input.slice(startPos, pos + 2).match(/^[0-7]+/);
let octalStr = match[0];
let octal = parseInt(octalStr, 8);
if (octal > 255) {
octalStr = octalStr.slice(0, -1);
octal = parseInt(octalStr, 8);
}
pos += octalStr.length - 1;
const next = input.charCodeAt(pos);
if (octalStr !== "0" || next === 56 || next === 57) {
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(startPos, lineStart, curLine);
}
}
return res(String.fromCharCode(octal));
}
return res(String.fromCharCode(ch));
}
}
function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
const initialPos = pos;
let n;
({
n,
pos
} = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors, !throwOnInvalid));
if (n === null) {
if (throwOnInvalid) {
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
} else {
pos = initialPos - 1;
}
}
return {
code: n,
pos
};
}
function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors, bailOnError) {
const start = pos;
const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
let invalid = false;
let total = 0;
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
const code = input.charCodeAt(pos);
let val;
if (code === 95 && allowNumSeparator !== "bail") {
const prev = input.charCodeAt(pos - 1);
const next = input.charCodeAt(pos + 1);
if (!allowNumSeparator) {
if (bailOnError) return {
n: null,
pos
};
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
} else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
if (bailOnError) return {
n: null,
pos
};
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
}
++pos;
continue;
}
if (code >= 97) {
val = code - 97 + 10;
} else if (code >= 65) {
val = code - 65 + 10;
} else if (_isDigit(code)) {
val = code - 48;
} else {
val = Infinity;
}
if (val >= radix) {
if (val <= 9 && bailOnError) {
return {
n: null,
pos
};
} else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
val = 0;
} else if (forceLen) {
val = 0;
invalid = true;
} else {
break;
}
}
++pos;
total = total * radix + val;
}
if (pos === start || len != null && pos - start !== len || invalid) {
return {
n: null,
pos
};
}
return {
n: total,
pos
};
}
function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
const ch = input.charCodeAt(pos);
let code;
if (ch === 123) {
++pos;
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
++pos;
if (code !== null && code > 0x10ffff) {
if (throwOnInvalid) {
errors.invalidCodePoint(pos, lineStart, curLine);
} else {
return {
code: null,
pos
};
}
}
} else {
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
}
return {
code,
pos
};
}
//# sourceMappingURL=index.js.map
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment