Merge branch 'dev' into container_monitor

This commit is contained in:
Helena Rasche
2020-04-20 17:01:32 +02:00
committed by GitHub
925 changed files with 24040 additions and 51558 deletions
+1
View File
@@ -0,0 +1 @@
api
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --skip_flakey_fails -api "$@"
+1
View File
@@ -0,0 +1 @@
framework
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --framework "$@"
+1
View File
@@ -0,0 +1 @@
integration
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
DOCKER_RUN_EXTRA_ARGS="--privileged" ./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --integration "$@"
+1
View File
@@ -0,0 +1 @@
main-tools
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc -main "$@"
+2 -3
View File
@@ -20,8 +20,7 @@ virtualenv "$GALAXY_VIRTUAL_ENV"
chown -R "$GALAXY_TEST_UID:$GALAXY_TEST_UID" "$GALAXY_VIRTUAL_ENV"
cd /galaxy
HOME=/galaxy
sudo -E -u "#${GALAXY_TEST_UID}" ./scripts/common_startup.sh || { echo "common_startup.sh failed"; exit 1; }
sudo -E -H -u "#${GALAXY_TEST_UID}" ./scripts/common_startup.sh || { echo "common_startup.sh failed"; exit 1; }
echo "Waiting for postgres to become available"
while ! nc -z postgres 5432;
@@ -34,7 +33,7 @@ echo "Creating postgres database for Galaxy"
createdb -w -U postgres -h postgres galaxy
echo "Starting and waiting for Galaxy daemon(s)"
sudo -E -u "#${GALAXY_TEST_UID}" GALAXY_RUN_ALL=1 bash "$GALAXY_ROOT/run.sh" --daemon --wait
sudo -E -H -u "#${GALAXY_TEST_UID}" GALAXY_RUN_ALL=1 bash "$GALAXY_ROOT/run.sh" --daemon --wait
echo "Galaxy daemon ready, monitoring Galaxy logs"
tail -f "$GALAXY_ROOT/main.log"
+1
View File
@@ -0,0 +1 @@
selenium
-26
View File
@@ -1,26 +0,0 @@
#!/bin/bash
# Enable retries on tests to reduce chances of transient failures.
: ${GALAXY_TEST_SELENIUM_RETRIES:=1}
# If in Jenkins environment, use it for artifacts.
if [ -n "$BUILD_NUMBER" ];
then
: ${GALAXY_TEST_ERRORS_DIRECTORY:=${BUILD_NUMBER}-test-errors}
: ${GALAXY_TEST_SCREENSHOTS_DIRECTORY:=${BUILD_NUMBER}-test-screenshots}
else
: ${GALAXY_TEST_ERRORS_DIRECTORY:=database/test-errors}
: ${GALAXY_TEST_SCREENSHOTS_DIRECTORY:=database/test-screenshots}
fi
mkdir -p "$GALAXY_TEST_ERRORS_DIRECTORY"
mkdir -p "$GALAXY_TEST_SCREENSHOTS_DIRECTORY"
mkdir -p ~/.jenkins-yarn-cache
YARN_CACHE_FOLDER=~/.jenkins-yarn-cache
# Start Selenium server in the test Docker container.
DOCKER_RUN_EXTRA_ARGS="${DOCKER_RUN_EXTRA_ARGS} --shm-size=2g -v $YARN_CACHE_FOLDER:$YARN_CACHE_FOLDER -e YARN_CACHE_FOLDER=$YARN_CACHE_FOLDER -e USE_SELENIUM=1 -e GALAXY_TEST_SELENIUM_RETRIES=${GALAXY_TEST_SELENIUM_RETRIES} -e GALAXY_TEST_ERRORS_DIRECTORY=${GALAXY_TEST_ERRORS_DIRECTORY} -e GALAXY_TEST_SCREENSHOTS_DIRECTORY=${GALAXY_TEST_SCREENSHOTS_DIRECTORY}"
export DOCKER_RUN_EXTRA_ARGS
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --skip_flakey_fails --selenium "$@"
+6 -1
View File
@@ -95,7 +95,8 @@ jobs:
<<: *set_workdir
steps:
- *restore_repo_cache
- *install_tox
# Ensure minimum virtualenv version due to https://github.com/pypa/virtualenv/issues/1670
- run: sudo pip install tox 'virtualenv>=20.0.8'
- run: tox -e py27-unit
py35_docstring:
docker:
@@ -135,6 +136,10 @@ jobs:
<<: *set_workdir
steps:
- *restore_repo_cache
- run: sh scripts/common_startup.sh
- run: wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0127.sqlite
- run: mv db_gx_rev_0127.sqlite database/universe.sqlite
- run: sh manage_db.sh -c ./config/galaxy.yml.sample upgrade
- *install_tox
- run: tox -e py35-first_startup
validate_test_tools:
+34
View File
@@ -0,0 +1,34 @@
name: Converter tests
on: [push, pull_request]
jobs:
test:
name: Test
runs-on: ubuntu-18.04
strategy:
matrix:
python-version: [3.7]
steps:
- uses: actions/checkout@v2
with:
path: 'galaxy root'
- name: Clone galaxyproject/galaxy-test-data
uses: actions/checkout@v2
with:
repository: galaxyproject/galaxy-test-data
path: galaxy-test-data
- uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Cache venv dir
uses: actions/cache@v1
id: pip-cache
with:
path: ~/.cache/pip
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('galaxy root/requirements.txt') }}
- name: Move test data
run: rsync -av --remove-source-files --exclude .git galaxy-test-data/ 'galaxy root/test-data/'
- name: Install planemo
run: pip install planemo
- name: Run tests
run: planemo test --galaxy_python_version ${{ matrix.python-version }} --galaxy_root 'galaxy root' 'galaxy root'/lib/galaxy/datatypes/converters/*xml
+16 -5
View File
@@ -2,8 +2,8 @@ name: Integration
on: [push, pull_request]
env:
GALAXY_TEST_DBURI: 'postgres://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
GALAXY_TEST_AMQP_URL: 'amqp://localhost:5672//'
jobs:
test:
name: Test
runs-on: ubuntu-18.04
@@ -20,11 +20,22 @@ jobs:
POSTGRES_DB: postgres
ports:
- 5432:5432
rabbitmq:
image: rabbitmq
ports:
- 5672:5672
steps:
- name: Prune unused docker image, volumes and containers
run: docker system prune -a -f
- name: Clean dotnet folder for space
if: matrix.subset == 'kubernetes'
run: rm -Rf /usr/share/dotnet
- name: Setup Minikube
if: matrix.subset == 'kubernetes'
id: minikube
uses: CodingNagger/minikube-setup-action@v1.0.2
uses: CodingNagger/minikube-setup-action@v1.0.3
with:
minikube-version: "1.9.0-0_amd64"
- name: Launch Minikube
if: matrix.subset == 'kubernetes'
run: eval ${{ steps.minikube.outputs.launcher }}
@@ -32,9 +43,8 @@ jobs:
if: matrix.subset == 'kubernetes'
run: |
kubectl get pods
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
fetch-depth: 1
path: 'galaxy root'
- uses: actions/setup-python@v1
with:
@@ -44,6 +54,7 @@ jobs:
id: pip-cache
with:
path: ~/.cache/pip
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('galaxy root/requirements.txt') }}
- name: Run tests
run: './run_tests.sh -integration test/integration -- -k "${{ matrix.subset }}"'
working-directory: 'galaxy root'
+9 -8
View File
@@ -9,19 +9,20 @@ jobs:
matrix:
python-version: [3.7]
steps:
- name: Cache tox dir
uses: actions/cache@v1
id: cache-tox-mulled
- uses: actions/checkout@v2
with:
path: .tox/mulled
key: tox-mulled-${{ matrix.python-version }}
- uses: actions/checkout@v1
with:
fetch-depth: 1
path: 'galaxy root'
- uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip dir
uses: actions/cache@v1
id: pip-cache
with:
path: ~/.cache/pip
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('galaxy root/requirements.txt') }}
- name: Install tox
run: pip install tox
- name: run tests
run: tox -e mulled
working-directory: 'galaxy root'
+28
View File
@@ -0,0 +1,28 @@
name: OSX
on: [push, pull_request]
jobs:
test:
name: Startup test
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
path: 'galaxy root'
- name: Cache pip dir
uses: actions/cache@v1
id: pip-cache
with:
path: ~/Library/Caches/pip
# scripts/common_startup.sh creates a conda env for Galaxy containing Python 3.6
key: pip-cache-3.6-${{ hashFiles('galaxy root/requirements.txt') }}
- name: Install tox
run: pip install tox
- name: Install and activate miniconda # use this job to test using Python from a conda environment
uses: goanpeca/setup-miniconda@v1
with:
activate-environment: ''
- name: run tests
run: tox -e first_startup
shell: bash -l {0} # need this to have CONDA_EXE set
working-directory: 'galaxy root'
+37
View File
@@ -0,0 +1,37 @@
name: Toolshed
on: [push, pull_request]
env:
GALAXY_TEST_DBURI: 'postgres://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
TOOL_SHED_TEST_DBURI: 'postgres://postgres:postgres@localhost:5432/toolshed?client_encoding=utf8'
jobs:
test:
name: Test
runs-on: ubuntu-18.04
strategy:
matrix:
python-version: [3.7]
services:
postgres:
image: postgres:11
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432:5432
steps:
- uses: actions/checkout@v2
with:
path: 'galaxy root'
- uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip dir
uses: actions/cache@v1
id: pip-cache
with:
path: ~/.cache/pip
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('galaxy root/requirements.txt') }}
- name: Run tests
run: './run_tests.sh -toolshed'
working-directory: 'galaxy root'
+2
View File
@@ -176,3 +176,5 @@ packages/*/*.egg-info
# Plugin build artifacts -- TODO make this more generic as a part of the standard plugin interface
config/plugins/visualizations/**/static/script.js
# TODO: Really need to follow up on this and make it standard.
config/plugins/visualizations/**/static/main.css
-30
View File
@@ -1,30 +0,0 @@
os: osx
# No version of Python is available via virtualenv on OS X workers, see https://github.com/travis-ci/travis-ci/issues/2312
language: generic
env:
- TOX_ENV=py27-first_startup
- TOX_ENV=py35-first_startup
install:
- set -e
- pip install tox
- |
if [ "$TOX_ENV" == "py27-first_startup" ]; then
sh scripts/common_startup.sh
wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0127.sqlite
mv db_gx_rev_0127.sqlite database/universe.sqlite
sh manage_db.sh upgrade
elif [ "$TOX_ENV" == "py35-first_startup" ]; then
MINICONDA_URL="https://repo.anaconda.com/miniconda"
MINICONDA_FILE="Miniconda3-latest-MacOSX-x86_64.sh"
curl -L -O "${MINICONDA_URL}/${MINICONDA_FILE}"
bash $MINICONDA_FILE -b
~/miniconda3/bin/conda create --yes --override-channels --channel conda-forge --channel defaults --name _galaxy_ 'python=3.5' 'pip>=9' 'virtualenv>=16'
. ~/miniconda3/bin/activate _galaxy_
fi
script: tox -e $TOX_ENV
notifications:
email: false
+1
View File
@@ -86,6 +86,7 @@ The following individuals have contributed code to Galaxy:
* Joachim Jacob <joachim.jacob@gmail.com>
* Xiaoqian Jiang <jxq198409@hotmail.com>
* Jim Johnson <jj@umn.edu> <jj@msi.umn.edu>
* Kaivan Kamali <kxk302@gmail.com>
* Radhesh Kamath <radhesh@bx.psu.edu>
* Iyad Kandalaft <ik@iyadk.com>
* Jan Kanis <jan.code@jankanis.nl>
+8 -5
View File
@@ -1,7 +1,7 @@
module.exports = {
extends: [
"eslint:recommended",
"plugin:vue/strongly-recommended",
"plugin:vue/strongly-recommended"
//"airbnb-base", eventually
],
env: {
@@ -15,17 +15,20 @@ module.exports = {
parser: "babel-eslint",
sourceType: "module"
},
plugins: ["html"],
rules: {
// Standard rules
"no-console": "off",
"no-unused-vars": ["error", { args: "none" }],
"prefer-const": "error",
"one-var": ["error", "never"],
"vue/valid-v-slot": "error",
"vue/v-slot-style": ["error", { atComponent: "v-slot", default: "v-slot", named: "longform" }],
// Vue TODO (enable these)
"vue/require-default-prop": "off",
"vue/require-prop-types": "off",
"vue/prop-name-casing": "off",
"vue/require-default-prop": "warn",
"vue/require-prop-types": "warn",
"vue/prop-name-casing": "warn",
// Prettier compromises/workarounds -- mostly #wontfix?
"vue/html-indent": "off",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 836 B

+11 -8
View File
@@ -39,7 +39,7 @@ try {
}
/** initalize options and sub-components */
GalaxyApp.prototype._init = function(options, bootstrapped) {
GalaxyApp.prototype._init = function (options, bootstrapped) {
_.extend(this, Backbone.Events);
if (localDebugging) {
this.logger = console;
@@ -98,7 +98,7 @@ GalaxyApp.prototype.defaultOptions = {
patchExisting: true,
/** root url of this app */
root: "/",
session_csrf_token: null
session_csrf_token: null,
};
/** filter to options present in defaultOptions (and default to them) */
@@ -162,8 +162,8 @@ GalaxyApp.prototype._initLogger = function _initLogger(loggerOptions) {
this.logger = new metricsLogger.MetricsLogger(loggerOptions);
this.emit = {};
["log", "debug", "info", "warn", "error", "metric"].map(i => {
this.emit[i] = data => {
["log", "debug", "info", "warn", "error", "metric"].map((i) => {
this.emit[i] = (data) => {
this.logger.emit(i, arguments[0], Array.prototype.slice.call(arguments, 1));
};
});
@@ -188,7 +188,10 @@ GalaxyApp.prototype._initLocale = function _initLocale(options) {
/** add the localize fn to this object and the window namespace (as '_l') */
GalaxyApp.prototype._initUserLocale = function _initUserLocale(options) {
// Choose best locale
const global_locale = this.config.default_locale ? this.config.default_locale.toLowerCase() : false;
const global_locale =
this.config.default_locale && this.config.default_locale != "auto"
? this.config.default_locale.toLowerCase()
: false;
let extra_user_preferences = {};
if (this.user && this.user.attributes.preferences && "extra_user_preferences" in this.user.attributes.preferences) {
@@ -209,7 +212,7 @@ GalaxyApp.prototype._initUserLocale = function _initUserLocale(options) {
? "__root"
: (navigator.language || navigator.userLanguage || "__root").toLowerCase();
const locale = user_locale || global_locale || nav_locale;
const locale = user_locale || nav_locale || global_locale;
sessionStorage.setItem("currentLocale", locale);
};
@@ -237,7 +240,7 @@ GalaxyApp.prototype._setUpListeners = function _setUpListeners() {
this.lastAjax = {
url: location.href.slice(0, -1) + options.url,
data: data
data: data,
};
//TODO:?? we might somehow manage to *retry* ajax using either this hook or Backbone.sync
});
@@ -298,5 +301,5 @@ GalaxyApp.prototype.toString = function toString() {
// ============================================================================
export default {
GalaxyApp: GalaxyApp
GalaxyApp: GalaxyApp,
};
+4 -4
View File
@@ -8,20 +8,20 @@ import { serverPath } from "utils/serverPath";
const galaxyStub = {
root: getAppRoot(),
config: {}
config: {},
};
if (!window.Galaxy) {
Object.defineProperty(window, "Galaxy", {
enumerable: true,
get: function() {
get: function () {
console.warn("accessing (get) window.Galaxy", serverPath());
return getGalaxyInstance() || galaxyStub;
},
set: function(newValue) {
set: function (newValue) {
console.warn("accessing (set) window.Galaxy", serverPath());
setGalaxyInstance(newValue);
}
},
});
} else {
console.error("Detected redefinition of window.Galaxy -- skipping, but this should be investigated.", serverPath());
+6 -6
View File
@@ -52,14 +52,14 @@ export function multiHistory(options) {
order: options.order,
limitOnFirstFetch: options.limit,
limitPerFetch: options.limit,
currentHistoryId: options.current_history_id
currentHistoryId: options.current_history_id,
});
const multipanel = new MultiPanel.MultiPanelColumns({
el: $("#center").get(0),
histories: histories
histories: histories,
});
histories.fetchFirst({ silent: true }).done(function() {
histories.fetchFirst({ silent: true }).done(function () {
multipanel.createColumns();
multipanel.render(0);
});
@@ -85,7 +85,7 @@ export function chart(options) {
export const chartUtilities = {
Datasets: Datasets,
Jobs: Jobs,
Series: Series
Series: Series,
};
export { initMasthead } from "components/Masthead/initMasthead";
@@ -93,9 +93,9 @@ export { panelManagement } from "onload/globalInits/panelManagement";
export { mountMakoTags } from "components/Tags";
export { mountJobMetrics } from "components/JobMetrics";
export { mountJobParameters } from "components/JobParameters";
export { mountWorkflowEditor, mountWorkflowPanel } from "components/Workflow/Editor/mount";
export { mountToolBoxWorkflow } from "components/Panels/mount";
export { mountWorkflowEditor } from "components/Workflow/Editor/mount";
export { mountPageDisplay } from "components/PageDisplay";
export { mountDestinationParams } from "components/JobDestinationParams";
// Used in common.mako
export { default as store } from "storemodern";
+7 -7
View File
@@ -13,21 +13,21 @@ export default {
*/
message: {
type: String,
default: ""
default: "",
},
/**
* Alias for variant, takes precedence if both are set
*/
status: {
type: String,
default: ""
default: "",
},
/**
* Alert type, one of done/success, info, warning, error/danger
*/
variant: {
type: String,
default: "done"
default: "done",
},
/** Display a close button in the alert that allows it to be dismissed */
dismissible: Boolean,
@@ -36,7 +36,7 @@ export default {
/** If a number, number of seconds to show before dismissing */
show: [Boolean, Number],
/** Should the alert fade out */
fade: Boolean
fade: Boolean,
},
computed: {
galaxyKwdToBootstrap() {
@@ -48,7 +48,7 @@ export default {
done: "success",
info: "info",
warning: "warning",
error: "danger"
error: "danger",
};
if (variant in galaxyKwdToBoostrapDict) {
return galaxyKwdToBoostrapDict[variant];
@@ -66,7 +66,7 @@ export default {
} else {
return false;
}
}
}
},
},
};
</script>
+108 -78
View File
@@ -1,33 +1,52 @@
<template>
<b-card>
<template v-slot:header>
<h4 class="mb-0">
Citations
<b-button
v-if="viewRender"
@click="toggleViewRender"
title="Show all in BibTeX format."
class="citations-to-bibtex"
>
<i class="fa fa-pencil-square-o"></i> Show BibTeX
</b-button>
<b-button v-else @click="toggleViewRender" title="Return to formatted citation list.">
<i class="fa fa-times"></i> Hide BibTeX
</b-button>
</h4>
</template>
<div v-if="source === 'histories'" class="infomessage">
When writing up your analysis, remember to include all references that should be cited in order to
completely describe your work. Also, please remember to
<a href="https://galaxyproject.org/citing-galaxy">cite Galaxy</a>.
</div>
<span v-if="viewRender" class="citations-formatted"> <p v-html="formattedReferences"></p> </span>
<pre v-else>
<div>
<b-card v-if="!simple">
<template v-slot:header>
<h4 class="mb-0">
Citations
<b-button
v-if="viewRender"
title="Show all in BibTeX format."
class="citations-to-bibtex"
@click="toggleViewRender"
>
<i class="fa fa-pencil-square-o"></i> Show BibTeX
</b-button>
<b-button v-else title="Return to formatted citation list." @click="toggleViewRender">
<i class="fa fa-times"></i> Hide BibTeX
</b-button>
</h4>
</template>
<div v-if="source === 'histories'" class="infomessage">
When writing up your analysis, remember to include all references that should be cited in order to
completely describe your work. Also, please remember to
<a href="https://galaxyproject.org/citing-galaxy">cite Galaxy</a>.
</div>
<span v-if="viewRender" class="citations-formatted">
<p v-html="formattedReferences"></p>
</span>
<pre v-else>
<code class="citations-bibtex">
{{ content }}
</code>
</pre>
</b-card>
</b-card>
<div v-else-if="citations.length">
<b-btn v-b-toggle="id" variant="primary">Citations</b-btn>
<b-collapse
:id="id.replace(/ /g, '_')"
class="mt-2"
@show="$emit('show')"
@shown="$emit('shown')"
@hide="$emit('hide')"
@hidden="$emit('hidden')"
>
<b-card>
<p v-html="formattedReferences"></p>
</b-card>
</b-collapse>
</div>
</div>
</template>
<script>
import _ from "underscore";
@@ -45,123 +64,134 @@ export default {
props: {
source: {
type: String,
required: true
required: true,
},
id: {
type: String,
required: true
required: true,
},
viewRender: {
type: Boolean,
requried: false,
default: true
}
default: true,
},
simple: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
citations: [],
content: "",
errors: []
errors: [],
showCollapse: false,
};
},
computed: {
formattedReferences: function() {
formattedReferences() {
return this.citations.reduce(
(a, b) => a.concat(`<p class="formatted-reference">${this.formattedReference(b)}</p>`),
""
);
}
},
},
created: function() {
updated() {
this.$nextTick(() => {
this.$emit("rendered");
});
},
created() {
axios
.get(`${getAppRoot()}api/${this.source}/${this.id}/citations`)
.then(response => {
.then((response) => {
this.content = "";
for (const rawCitation of response.data) {
response.data.forEach((rawCitation) => {
try {
const citation = {
fields: {},
entryType: undefined
entryType: undefined,
};
let parsed = bibtexParse.toJSON(rawCitation.content);
if (parsed) {
parsed = _.first(parsed);
citation.entryType = parsed.entryType || undefined;
for (const key in parsed.entryTags) {
Object.keys(parsed.entryTags).forEach((key) => {
citation.fields[key.toLowerCase()] = parsed.entryTags[key];
}
});
}
this.citations.push(citation);
this.content += rawCitation.content;
} catch (err) {
console.warn("Error parsing bibtex: " + err);
console.warn(`Error parsing bibtex: ${err}`);
}
}
});
})
.catch(e => {
.catch((e) => {
console.error(e);
});
},
methods: {
formattedReference: function(citation) {
const entryType = citation.entryType;
const fields = citation.fields;
formattedReference(citation) {
const { entryType, fields } = citation;
let ref = "";
const authorsAndYear = `${this._asSentence(
const authorsAndYear = `${this.asSentence(
(fields.author ? fields.author : "") + (fields.year ? ` (${fields.year})` : "")
)} `;
const title = fields.title || "";
const pages = fields.pages ? `pp. ${fields.pages}` : "";
const address = fields.address;
if (entryType == "article") {
const { address } = fields;
if (entryType === "article") {
const volume =
(fields.volume ? fields.volume : "") +
(fields.number ? ` (${fields.number})` : "") +
(pages ? `, ${pages}` : "");
ref = `${authorsAndYear +
this._asSentence(title) +
ref = `${
authorsAndYear +
this.asSentence(title) +
(fields.journal ? `In <em>${fields.journal}, ` : "") +
this._asSentence(volume) +
this._asSentence(fields.address)}</em>`;
} else if (entryType == "inproceedings" || entryType == "proceedings") {
ref = `${authorsAndYear +
this._asSentence(title) +
this.asSentence(volume) +
this.asSentence(fields.address)
}</em>`;
} else if (entryType === "inproceedings" || entryType === "proceedings") {
ref = `${
authorsAndYear +
this.asSentence(title) +
(fields.booktitle ? `In <em>${fields.booktitle}, ` : "") +
(pages ? pages : "") +
(address ? `, ${address}` : "")}.</em>`;
} else if (entryType == "mastersthesis" || entryType == "phdthesis") {
(pages || "") +
(address ? `, ${address}` : "")
}.</em>`;
} else if (entryType === "mastersthesis" || entryType === "phdthesis") {
ref =
authorsAndYear +
this._asSentence(title) +
this.asSentence(title) +
(fields.howpublished ? `${fields.howpublished}. ` : "") +
(fields.note ? `${fields.note}.` : "");
} else if (entryType == "techreport") {
} else if (entryType === "techreport") {
ref =
authorsAndYear +
this._asSentence(title) +
this._asSentence(fields.institution) +
this._asSentence(fields.number) +
this._asSentence(fields.type);
} else if (entryType == "book" || entryType == "inbook" || entryType == "incollection") {
ref = `${authorsAndYear} ${this._formatBookInfo(fields)}`;
this.asSentence(title) +
this.asSentence(fields.institution) +
this.asSentence(fields.number) +
this.asSentence(fields.type);
} else if (entryType === "book" || entryType === "inbook" || entryType === "incollection") {
ref = `${authorsAndYear} ${this.formatBookInfo(fields)}`;
} else {
ref = `${authorsAndYear} ${this._asSentence(title)}${this._asSentence(
ref = `${authorsAndYear} ${this.asSentence(title)}${this.asSentence(
fields.howpublished
)}${this._asSentence(fields.note)}`;
)}${this.asSentence(fields.note)}`;
}
let doiUrl = "";
if (fields.doi) {
doiUrl = `https://doi.org/${fields.doi}`;
ref += `[<a href="${doiUrl}" target="_blank">doi:${fields.doi}</a>]`;
ref += `[<a href="https://doi.org/${fields.doi}" target="_blank">doi:${fields.doi}</a>]`;
}
const url = fields.url || doiUrl;
if (url) {
ref += `[<a href="${url}" target="_blank">Link</a>]`;
if (fields.url) {
ref += `[<a href="${fields.url}" target="_blank">Link</a>]`;
}
return convertLaTeX({ onError: (error, latex) => `{${stringifyLaTeX(latex)}}` }, ref);
},
_formatBookInfo: function(fields) {
formatBookInfo(fields) {
let info = "";
if (fields.chapter) {
info += `${fields.chapter} in `;
@@ -189,13 +219,13 @@ export default {
}
return `${info}.`;
},
_asSentence: function(str) {
asSentence(str) {
return str && str.trim() ? `${str}. ` : "";
},
toggleViewRender: function() {
toggleViewRender() {
this.viewRender = !this.viewRender;
}
}
},
},
};
</script>
<style>
@@ -30,35 +30,35 @@ library.add(faTimesCircle);
export default {
components: {
FontAwesomeIcon
FontAwesomeIcon,
},
props: {
query: {
type: String
type: String,
},
loading: {
type: Boolean
type: Boolean,
},
placeholder: {
type: String,
default: "Enter your search term here."
default: "Enter your search term here.",
},
delay: {
type: Number,
default: 1000
}
default: 1000,
},
},
data() {
return {
queryInput: null,
queryTimer: null,
queryCurrent: null
queryCurrent: null,
};
},
watch: {
query(queryNew) {
this.setQuery(queryNew);
}
},
},
methods: {
clearTimer() {
@@ -82,7 +82,7 @@ export default {
this.queryCurrent = this.queryInput = queryNew;
this.$emit("onChange", this.queryCurrent);
}
}
}
},
},
};
</script>
@@ -5,20 +5,20 @@
import StatelessTags from "components/Tags/StatelessTags";
export default {
components: {
StatelessTags
StatelessTags,
},
props: {
index: {
type: Number
type: Number,
},
tags: {
type: Array
}
type: Array,
},
},
methods: {
onInput(tags) {
this.$emit("input", tags, this.index);
}
}
},
},
};
</script>
@@ -11,7 +11,7 @@ const mockOptions = {
host: "host",
root: "root",
history: "history",
modalStatic: true
modalStatic: true,
};
describe("model.js", () => {
@@ -92,7 +92,7 @@ describe("services.js/Services", () => {
hid: 1,
id: 1,
history_id: 0,
name: "name_1"
name: "name_1",
};
const services = new Services(mockOptions);
const items = services.getItems(rawData);
@@ -111,19 +111,19 @@ describe("DataDialog.vue", () => {
id: 1,
hid: 1,
name: "dataset_1",
history_content_type: "dataset"
history_content_type: "dataset",
},
{
id: 2,
name: "dataset_2",
type: "file"
type: "file",
},
{
id: 3,
hid: 3,
name: "collection_1",
history_content_type: "dataset_collection"
}
history_content_type: "dataset_collection",
},
];
const mockServices = class {
@@ -144,7 +144,7 @@ describe("DataDialog.vue", () => {
wrapper = mount(DataDialog, {
propsData: mockOptions,
attachToDocument: true,
localVue
localVue,
});
});
@@ -52,32 +52,32 @@ export default {
props: {
multiple: {
type: Boolean,
default: false
default: false,
},
format: {
type: String,
default: "download"
default: "download",
},
library: {
type: Boolean,
default: true
default: true,
},
root: {
type: String,
required: true
required: true,
},
host: {
type: String,
required: true
required: true,
},
history: {
type: String,
required: true
required: true,
},
modalStatic: {
type: Boolean,
default: false
}
default: false,
},
},
data() {
return {
@@ -87,10 +87,10 @@ export default {
modalShow: true,
optionsShow: false,
undoShow: false,
hasValue: false
hasValue: false,
};
},
created: function() {
created: function () {
this.services = new Services({ root: this.root, host: this.host });
this.urlTracker = new UrlTracker(this.getHistoryUrl());
this.model = new Model({ multiple: this.multiple, format: this.format });
@@ -98,7 +98,7 @@ export default {
},
methods: {
/** Returns the default url i.e. the url of the current history **/
getHistoryUrl: function() {
getHistoryUrl: function () {
return `${this.root}api/histories/${this.history}/contents?deleted=false`;
},
/** Add highlighting for record variations, i.e. datasets vs. libraries/collections **/
@@ -112,7 +112,7 @@ export default {
}
},
/** Collects selected datasets in value array **/
clicked: function(record) {
clicked: function (record) {
if (record.isLeaf) {
this.model.add(record);
this.hasValue = this.model.count() > 0;
@@ -126,34 +126,34 @@ export default {
}
},
/** Called when selection is complete, values are formatted and parsed to external callback **/
finalize: function() {
finalize: function () {
const results = this.model.finalize();
this.modalShow = false;
this.callback(results);
},
/** Performs server request to retrieve data records **/
load: function(url) {
load: function (url) {
url = this.urlTracker.getUrl(url);
this.filter = null;
this.optionsShow = false;
this.undoShow = !this.urlTracker.atRoot();
this.services
.get(url)
.then(items => {
.then((items) => {
if (this.library && this.urlTracker.atRoot()) {
items.unshift({
label: "Data Libraries",
url: `${this.root}api/libraries`
url: `${this.root}api/libraries`,
});
}
this.items = items;
this.formatRows();
this.optionsShow = true;
})
.catch(errorMessage => {
.catch((errorMessage) => {
this.errorMessage = errorMessage;
});
}
}
},
},
};
</script>
@@ -35,7 +35,7 @@ export class Model {
/** Finalizes the results from added records **/
finalize() {
let results = [];
Object.values(this.values).forEach(v => {
Object.values(this.values).forEach((v) => {
let value = null;
if (this.format) {
value = v[this.format];
@@ -11,11 +11,11 @@ export class Services {
return new Promise((resolve, reject) => {
axios
.get(url)
.then(response => {
.then((response) => {
const items = this.getItems(response.data);
resolve(items);
})
.catch(e => {
.catch((e) => {
let errorMessage = "Request failed.";
if (e.response) {
errorMessage = e.response.data.err_msg || `${e.response.statusText} (${e.response.status})`;
@@ -32,7 +32,7 @@ export class Services {
while (stack.length > 0) {
const root = stack.pop();
if (Array.isArray(root)) {
root.forEach(element => {
root.forEach((element) => {
stack.push(element);
});
} else if (root.elements) {
@@ -8,13 +8,13 @@ import { mapGetters } from "vuex";
export default {
props: {
item: Object
item: Object,
},
computed: {
...mapGetters(["getHistoryNameById"]),
historyName() {
return this.getHistoryNameById(this.item.history_id);
}
}
},
},
};
</script>
@@ -48,7 +48,7 @@ export default {
LoadingSpan,
DelayedInput,
UtcDate,
Tags
Tags,
},
data() {
return {
@@ -57,31 +57,31 @@ export default {
fields: [
{
key: "name",
sortable: true
sortable: true,
},
{
key: "tags",
sortable: false
sortable: false,
},
{
label: "History",
key: "history_id",
sortable: true
sortable: true,
},
{
key: "extension",
sortable: true
sortable: true,
},
{
label: "Updated",
key: "update_time",
sortable: true
sortable: true,
},
{
key: "context",
label: "",
sortable: false
}
sortable: false,
},
],
query: "",
limit: 50,
@@ -91,7 +91,7 @@ export default {
loading: true,
message: null,
messageVariant: null,
rows: []
rows: [],
};
},
computed: {
@@ -103,7 +103,7 @@ export default {
},
showMessage() {
return !!this.message;
}
},
},
created() {
this.fetchHistories();
@@ -121,9 +121,9 @@ export default {
sortBy: this.sortBy,
sortDesc: this.sortDesc,
offset: this.offset,
limit: this.limit
limit: this.limit,
})
.then(datasets => {
.then((datasets) => {
if (concat) {
this.rows = this.rows.concat(datasets);
} else {
@@ -131,7 +131,7 @@ export default {
}
this.loading = false;
})
.catch(error => {
.catch((error) => {
this.error = error;
});
},
@@ -142,10 +142,10 @@ export default {
const history_id = history.model.id;
this.services
.copyDataset(dataset_id, history_id)
.then(response => {
.then((response) => {
history.loadCurrentHistory();
})
.catch(error => {
.catch((error) => {
this.onError(error);
});
},
@@ -153,17 +153,17 @@ export default {
const Galaxy = getGalaxyInstance();
this.services
.setHistory(item.history_id)
.then(history => {
.then((history) => {
Galaxy.currHistoryPanel.loadCurrentHistory();
})
.catch(error => {
.catch((error) => {
this.onError(error);
});
},
onTags(tags, index) {
const item = this.rows[index];
item.tags = tags;
this.services.updateTags(item.id, "HistoryDatasetAssociation", tags).catch(error => {
this.services.updateTags(item.id, "HistoryDatasetAssociation", tags).catch((error) => {
this.onError(error);
});
},
@@ -193,7 +193,7 @@ export default {
onError(message) {
this.message = message;
this.messageVariant = "danger";
}
}
},
},
};
</script>
@@ -42,7 +42,7 @@ Vue.use(BootstrapVue);
export default {
props: {
item: Object
item: Object,
},
computed: {
getName() {
@@ -53,7 +53,7 @@ export default {
},
isPaused() {
return this.item.state === "paused";
}
},
},
methods: {
copyDataset(item) {
@@ -61,8 +61,8 @@ export default {
},
showDataset(item) {
this.$emit("showDataset", this.item);
}
}
},
},
};
</script>
@@ -36,7 +36,7 @@ export class Services {
const response = await axios.post(url, {
type: "dataset",
source: "hda",
content: dataset_id
content: dataset_id,
});
return response.data;
} catch (e) {
@@ -60,7 +60,7 @@ export class Services {
const response = await axios.put(url, {
item_id: item_id,
item_class: item_class,
item_tags: item_tags
item_tags: item_tags,
});
return response.data;
} catch (e) {
@@ -20,18 +20,18 @@ export default {
props: {
id: {
type: String,
required: false
}
required: false,
},
},
data() {
return {
historyTemplate: "",
historyJSON: {},
// TODO: Error message standardization -- use bootstrap-vue component or the like.
errorMessages: []
errorMessages: [],
};
},
created: function() {
created: function () {
const historyId = this.id;
let url = "";
if (historyId !== undefined && historyId !== "" && historyId !== null) {
@@ -42,52 +42,52 @@ export default {
this.ajaxCall(url);
},
methods: {
ajaxCall: function(url) {
ajaxCall: function (url) {
axios
.get(url)
.then(response => {
.then((response) => {
this._updateHistoryData(response);
})
.catch(e => {
.catch((e) => {
console.error(e);
this.errorMessages.push(
"Error fetching history data -- reload the page to retry this request. Please contact an administrator if the problem persists."
);
});
},
_updateHistoryData: function(response) {
_updateHistoryData: function (response) {
const historyItems = response.data;
this.historyTemplate = historyItems.template;
this.historyJSON = historyItems.history_json;
},
makeHistoryView: function(historyDict) {
window.hdas = historyDict.map(hda => {
makeHistoryView: function (historyDict) {
window.hdas = historyDict.map((hda) => {
const Galaxy = getGalaxyInstance();
return new HDAListItemEdit.HDAListItemEdit({
model: new HDAModel.HistoryDatasetAssociation(hda),
el: $("#hda-" + hda.id),
linkTarget: "galaxy_main",
purgeAllowed: Galaxy.config.allow_user_dataset_purge,
logger: Galaxy.logger
logger: Galaxy.logger,
}).render(0);
});
// toggle the body section of each item in the structure
$(function() {
$(function () {
$(".workflow, .tool").each((index, element) => {
const body = $(element).children(".body");
$(element)
.children(".header")
.click(e => {
.click((e) => {
body.toggle();
})
.addClass("clickable");
});
});
}
},
},
updated: function() {
updated: function () {
this.makeHistoryView(this.historyJSON);
}
},
};
</script>
@@ -15,17 +15,17 @@ export default {
props: {
style: {
type: String,
default: "light"
default: "light",
},
size: {
type: Number,
default: 100
default: 100,
},
center: {
type: Boolean,
default: false
}
}
default: false,
},
},
};
</script>
@@ -23,21 +23,25 @@ import { getGalaxyInstance } from "app";
export default {
props: ["history"],
data() {
return {
urlView: `${getAppRoot()}histories/view?id=${this.history.id}`,
urlShowStructure: `${getAppRoot()}histories/show_structure?id=${this.history.id}`,
urlSharing: `${getAppRoot()}histories/sharing?id=${this.history.id}`
};
},
created() {
this.root = getAppRoot();
},
computed: {
urlView() {
return `${getAppRoot()}histories/view?id=${this.history.id}`;
},
urlShowStructure() {
return `${getAppRoot()}histories/show_structure?id=${this.history.id}`;
},
urlSharing() {
return `${getAppRoot()}histories/sharing?id=${this.history.id}`;
},
},
methods: {
onSwitch: function() {
onSwitch() {
const Galaxy = getGalaxyInstance();
Galaxy.currHistoryPanel.switchToHistory(this.history.id);
}
}
},
},
};
</script>
@@ -22,16 +22,16 @@ export default {
return {
sourceFile: null,
sourceURL: null,
errorMessage: null
errorMessage: null,
};
},
computed: {
hasErrorMessage() {
return this.errorMessage != null;
}
},
},
methods: {
submit: function(ev) {
submit: function (ev) {
ev.preventDefault();
if (!this.sourceFile && !this.sourceURL) {
this.errorMessage = "You must provide a history archive URL or file.";
@@ -41,17 +41,17 @@ export default {
formData.append("archive_source", this.sourceURL);
axios
.post(`${getAppRoot()}api/histories`, formData)
.then(response => {
.then((response) => {
window.location = `${getAppRoot()}histories/list?message=${
response.data.message
}&status=success`;
})
.catch(error => {
.catch((error) => {
const message = error.response.data && error.response.data.err_msg;
this.errorMessage = message || "Import failed for an unknown reason.";
});
}
}
}
},
},
};
</script>
@@ -47,45 +47,45 @@ export default {
props: {
id: {
type: String,
required: true
}
required: true,
},
},
data() {
return {
historyData: {},
historyHistory: {},
// TODO: Pick a standard messaging convention and use a child component for it here.
errorMessages: []
errorMessages: [],
};
},
created: function() {
created: function () {
const url = getAppRoot() + "history/view/" + this.id;
this.ajaxCall(url, this.updateHistoryView);
},
methods: {
ajaxCall: function(url, callBack) {
ajaxCall: function (url, callBack) {
axios
.get(url)
.then(response => {
.then((response) => {
callBack(response);
})
.catch(e => {
.catch((e) => {
this.showError(
"Error fetching histories -- reload the page to retry this request. Please contact an administrator if the problem persists.",
e
);
});
},
updateHistoryView: function(response) {
updateHistoryView: function (response) {
this.historyData = response.data;
this.historyHistory = response.data.history;
},
showError: function(errorMsg, verbose) {
showError: function (errorMsg, verbose) {
console.error(verbose);
this.errorMessages.push(errorMsg);
},
makeHistoryView: function(history) {
$(function() {
makeHistoryView: function (history) {
$(function () {
const options = {
hasMasthead: history.use_panels ? "true" : "false",
userIsOwner: history.user_is_owner ? "true" : "false",
@@ -95,7 +95,7 @@ export default {
showHiddenJson: history.show_hidden,
initialModeDeleted: history.show_deleted ? "showing_deleted" : "not_showing_deleted",
initialModeHidden: history.show_hidden ? "showing_hidden" : "not_showing_hidden",
allowUserDatasetPurge: history.allow_user_dataset_purge ? "true" : "false"
allowUserDatasetPurge: history.allow_user_dataset_purge ? "true" : "false",
};
options.viewToUse = options.userIsOwner
? { location: "mvc/history/history-view-edit", className: "HistoryViewEdit" }
@@ -103,24 +103,24 @@ export default {
HistoryView.historyEntry(options);
});
},
showStructure: function() {
showStructure: function () {
const Galaxy = getGalaxyInstance();
const displayStructureInstance = Vue.extend(DisplayStructure),
mountView = document.createElement("div");
const displayStructureInstance = Vue.extend(DisplayStructure);
const mountView = document.createElement("div");
Galaxy.page.center.display(mountView);
new displayStructureInstance({ propsData: { id: QueryStringParsing.get("id") } }).$mount(mountView);
},
switchHistory: function() {
switchHistory: function () {
const url = getAppRoot() + "history/switch_to_history?hist_id=" + this.historyHistory["id"];
this.ajaxCall(url, this.reloadPage);
},
reloadPage: function() {
reloadPage: function () {
window.location.reload();
}
},
},
updated: function() {
updated: function () {
this.makeHistoryView(this.historyData);
}
},
};
</script>
@@ -0,0 +1,73 @@
import InteractiveTools from "./InteractiveTools";
import { mount, createLocalVue } from "@vue/test-utils";
import _l from "utils/localization";
import Vue from "vue";
import testInteractiveToolsResponse from "./testData/testInteractiveToolsResponse";
import MockAdapter from "axios-mock-adapter";
import axios from "axios";
describe("ToolsView/ToolsView.vue", () => {
const localVue = createLocalVue();
localVue.filter("localize", (value) => _l(value));
let wrapper, emitted, axiosMock;
beforeEach(async () => {
axiosMock = new MockAdapter(axios);
wrapper = mount(InteractiveTools, {
computed: {
currentHistory() {
return {
loadCurrentHistory() {},
};
},
},
});
emitted = wrapper.emitted();
axiosMock.onGet("/api/entry_points?running=true").reply(200, testInteractiveToolsResponse);
axiosMock.onPost("/interactivetool/list").reply(200, { status: "ok", message: "ok" });
await Vue.nextTick();
await Vue.nextTick();
await Vue.nextTick();
});
afterEach(() => {
axiosMock.restore();
});
it("table should exist", async () => {
const table = wrapper.find("#workflow-table");
assert(table.exists() === true, "Interactive Tools table doesn't exist!");
});
it("selected tool should disappear after stop button pressed", async () => {
function checkIfExists(tag, toolId) {
return wrapper.find(tag + toolId).exists();
}
const toolId = testInteractiveToolsResponse[0].id;
const tool = wrapper.vm.activeInteractiveTools.find((tool) => tool.id === toolId);
assert(checkIfExists("#link-", toolId) === true, "tool is not rendered!");
// ensure that the tool is not marked
assert(tool.marked === undefined || false, "tool was marked before click!");
// mark & delete tool
const checkbox = wrapper.find("#checkbox-" + toolId);
checkbox.trigger("click");
await Vue.nextTick();
// ensure that the tool is marked
assert(tool.marked === true, "clicking on checkbox did not mark corresponding tool!");
const stopBtn = wrapper.find("#stopInteractiveTool");
stopBtn.trigger("click");
await Vue.nextTick();
await Vue.nextTick();
await Vue.nextTick();
const toolExists = wrapper.vm.activeInteractiveTools.includes((tool) => tool.id === toolId);
// ensure that the tool was deleted from the list
assert(toolExists === false, "tool was not deleted from the list!");
assert(checkIfExists("#link-", toolId) === false, "tool is rendered after deletion!");
});
});
@@ -0,0 +1,172 @@
<template>
<div>
<b-alert :name="message" :variant="messageVariant" :show="showMessage">{{ message }}</b-alert>
<h2>Active Interactive Tools</h2>
<b-row class="mb-3">
<b-col cols="6">
<b-input
id="workflow-search"
class="m-1"
name="query"
placeholder="Search Interactive Tool"
autocomplete="off"
type="text"
v-model="filter"
/>
</b-col>
</b-row>
<b-table
id="workflow-table"
striped
:fields="fields"
:items="activeInteractiveTools"
:filter="filter"
@filtered="filtered"
>
<template v-slot:cell(checkbox)="row">
<b-form-checkbox :id="createId('checkbox', row.item.id)" v-model="row.item.marked" />
</template>
<template v-slot:cell(name)="row">
<a
:index="row.index"
:id="createId('link', row.item.id)"
:href="row.item.target"
target="_blank"
:name="row.item.name"
>{{ row.item.name }}</a
>
</template>
<template v-slot:cell(job_info)="row">
<label v-if="row.item.active">
running
</label>
<label v-else>
stopped
</label>
</template>
<template v-slot:cell(created_time)="row">
<UtcDate :date="row.item.created_time" mode="elapsed" />
</template>
<template v-slot:cell(last_updated)="row">
<UtcDate :date="row.item.modified_time" mode="elapsed" />
</template>
</b-table>
<label v-if="isActiveToolsListEmpty">You do not have active interactive tools yet </label>
<div v-if="showNotFound">
No matching entries found for: <span class="font-weight-bold">{{ this.filter }}</span
>.
</div>
<b-button
id="stopInteractiveTool"
v-if="isCheckboxMarked"
v-b-tooltip.hover.bottom
title="Terminate selected tools"
@click.stop="stopInteractiveToolSession()"
>Stop
</b-button>
</div>
</template>
<script>
import { Services } from "./services";
import Vue from "vue";
import { getAppRoot } from "onload/loadConfig";
import UtcDate from "components/UtcDate";
import { getGalaxyInstance } from "app";
import BootstrapVue from "bootstrap-vue";
Vue.use(BootstrapVue);
export default {
components: {
UtcDate,
},
data() {
return {
error: null,
fields: [
{
label: "",
key: "checkbox",
},
{
label: "Name",
key: "name",
sortable: true,
},
{
label: "Job Info",
key: "job_info",
sortable: true,
},
{
label: "Created",
key: "created_time",
sortable: true,
},
{
label: "Last Updated",
key: "last_updated",
sortable: true,
},
],
filter: "",
message: null,
messageVariant: null,
nInteractiveTools: 0,
activeInteractiveTools: [],
};
},
computed: {
showNotFound() {
return this.nInteractiveTools === 0 && this.filter && !this.isActiveToolsListEmpty;
},
isCheckboxMarked() {
return this.activeInteractiveTools.some((tool) => tool.marked);
},
isActiveToolsListEmpty() {
return this.activeInteractiveTools.length === 0;
},
showMessage() {
return !!this.message;
},
currentHistory() {
return getGalaxyInstance().currHistoryPanel;
},
},
created() {
this.root = getAppRoot();
this.services = new Services({ root: this.root });
this.load();
},
methods: {
load() {
this.filter = "";
this.services
.getActiveInteractiveTools()
.then((activeInteractiveTools) => {
this.activeInteractiveTools = activeInteractiveTools;
})
.catch((error) => {
this.error = error;
});
},
filtered: function (items) {
this.nInteractiveTools = items.length;
},
stopInteractiveToolSession() {
const idsToStop = this.activeInteractiveTools.filter((tool) => tool.marked).map((tool) => tool.id);
this.services.stopInteractiveTool(idsToStop).then((response) => {
if (response.status === "ok") this.messageVariant = "success";
else this.messageVariant = "danger";
this.message = response.message;
this.activeInteractiveTools = this.activeInteractiveTools.filter((tool) => !tool.marked);
this.currentHistory.loadCurrentHistory();
});
},
createId(tagLabel, id) {
return tagLabel + "-" + id;
},
},
};
</script>
@@ -0,0 +1,31 @@
import axios from "axios";
import { rethrowSimple } from "utils/simple-error";
import { getAppRoot } from "onload/loadConfig";
export class Services {
constructor(options = {}) {
this.root = options.root || getAppRoot();
}
async getActiveInteractiveTools() {
const url = `${this.root}api/entry_points?running=true`;
try {
const response = await axios.get(url);
return response.data;
} catch (e) {
rethrowSimple(e);
}
}
async stopInteractiveTool(ids) {
const url = `${this.root}interactivetool/list`;
const formData = new FormData();
formData.append("operation", "stop");
ids.forEach((id) => formData.append("id", id));
const response = await axios.post(url, formData);
return response.data;
}
catch(e) {
rethrowSimple(e);
}
}
@@ -0,0 +1,20 @@
[
{
"model_class": "InteractiveToolEntryPoint",
"id": "52e496b945151ee8",
"name": "Jupyter Interactive Tool",
"active": true,
"created_time": "2020-02-24T15:59:18.122103",
"modified_time": "2020-02-24T15:59:20.428594",
"target": "http://52e496b945151ee8-be8a5bee5d5849a5b4e035b51305256e.interactivetoolentrypoint.interactivetool.localhost:8080/ipython/lab"
},
{
"model_class": "InteractiveToolEntryPoint",
"id": "b887d74393f85b6d",
"name": "AskOmics instance on None",
"active": true,
"created_time": "2020-01-24T15:59:22.406480",
"modified_time": "2020-02-24T15:59:24.757453",
"target": "http://b887d74393f85b6d-b1fd3f42331a49c1b3d8a4d1b27240b8.interactivetoolentrypoint.interactivetool.localhost:8080/loginapikey/oleg"
}
]
@@ -0,0 +1,67 @@
import Vuex from "vuex";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { mount, createLocalVue } from "@vue/test-utils";
import { createStore } from "../../store";
import flushPromises from "flush-promises";
import JobDestinationParams from "./JobDestinationParams";
import jobDestinationResponse from "./testData/jobDestinationResponse";
const JOB_ID = "foo_job_id";
describe("JobDestinationParams/JobDestinationParams.vue", () => {
const localVue = createLocalVue();
localVue.use(Vuex);
const responseKeys = Object.keys(jobDestinationResponse);
let testStore, axiosMock, wrapper;
beforeEach(async () => {
axiosMock = new MockAdapter(axios);
testStore = createStore();
const propsData = {
jobId: JOB_ID,
};
axiosMock.onGet(`/api/jobs/${JOB_ID}/destination_params`).reply(200, jobDestinationResponse);
wrapper = mount(JobDestinationParams, {
store: testStore,
propsData,
localVue,
});
await flushPromises();
assert(responseKeys.length > 0, "test data is invalid!");
});
afterEach(() => {
axiosMock.restore();
});
it("destination parameters should exist", async () => {
expect(Object.keys(wrapper.vm.jobDestinationParams).length).to.equals(responseKeys.length);
expect(wrapper.vm.jobId).to.equals(JOB_ID);
expect(wrapper.vm.jobDestinationParams["docker_net"]).to.equals("bridge");
expect(wrapper.vm.jobDestinationParams["docker_set_user"]).to.equals(null);
});
it("destination parameters should be rendered", async () => {
console.log(wrapper.html());
const paramsTable = wrapper.find("#destination_parameters");
expect(paramsTable.isVisible()).to.equals(true);
const params = paramsTable.findAll("tbody > tr");
expect(params.length).to.equals(responseKeys.length);
for (let counter = 0; counter < responseKeys.length - 1; counter++) {
const parameter = params.at(counter).findAll("td");
const parameterTitle = parameter.at(0).text();
const parameterValue = parameter.at(1).text();
assert(responseKeys.includes(parameterTitle), "rendered parameter should exist in test data!");
// since we render null as an empty string, rendered empty string should always equal null in test data
assert(
jobDestinationResponse[parameterTitle] === (parameterValue === "" ? null : parameterValue),
"parameter value is not equal to test data!"
);
}
});
});
@@ -0,0 +1,36 @@
<template>
<div>
<table id="destination_parameters" class="tabletip info_data_table">
<tbody>
<tr v-for="(value, title) in jobDestinationParams" :key="title">
<td>{{ title }}</td>
<td>{{ value }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { mapCacheActions } from "vuex-cache";
export default {
props: {
jobId: {
type: String,
required: true,
},
},
created: function () {
this.fetchJobDestinationParams(this.jobId);
},
computed: {
jobDestinationParams: function () {
return this.$store.getters.jobDestinationParams(this.jobId);
},
},
methods: {
...mapCacheActions(["fetchJobDestinationParams"]),
},
};
</script>
@@ -0,0 +1,3 @@
export { default as JobDestinationParams } from "./JobDestinationParams";
export { mountDestinationParams } from "./mount";
@@ -0,0 +1,13 @@
/**
* Endpoint for mounting job metrics from non-Vue environment.
*/
import $ from "jquery";
import JobDestinationParams from "./JobDestinationParams.vue";
import { mountVueComponent } from "utils/mountVueComponent";
export const mountDestinationParams = (propsData = {}) => {
$(".job-destination-parameters").each((index, el) => {
propsData.jobId = $(el).attr("job_id");
mountVueComponent(JobDestinationParams)(propsData, el);
});
};
@@ -0,0 +1,12 @@
{
"Runner": "local",
"Runner Job ID": "23027",
"Handler": "main.web.1",
"docker_auto_rm": "true",
"docker_enabled": "true",
"docker_net": "bridge",
"docker_set_user": null,
"docker_sudo": "false",
"docker_volumes": "$galaxy_root:ro,$tool_directory:ro,$job_directory:rw,$working_directory:rw,$default_file_path:ro",
"require_container": "true"
}
@@ -25,12 +25,12 @@ describe("JobMetrics/JobMetrics.vue", () => {
it("should not render a div if no plugins found in store", async () => {
const propsData = {
jobId: JOB_ID
jobId: JOB_ID,
};
const wrapper = mount(JobMetrics, {
store: testStore,
propsData,
localVue
localVue,
});
await wrapper.vm.$nextTick();
expect(wrapper.isEmpty()).to.equals(true);
@@ -38,18 +38,18 @@ describe("JobMetrics/JobMetrics.vue", () => {
it("should group plugins by type", async () => {
const propsData = {
jobId: JOB_ID
jobId: JOB_ID,
};
const metricsResponse = [
{ plugin: "core", title: "runtime", value: 145 },
{ plugin: "core", title: "memory", value: 146 },
{ plugin: "extended", title: "awesomeness", value: 42 }
{ plugin: "extended", title: "awesomeness", value: 42 },
];
axiosMock.onGet(`/api/jobs/${JOB_ID}/metrics`).reply(200, metricsResponse);
const wrapper = mount(JobMetrics, {
store: testStore,
propsData,
localVue
localVue,
});
// Wait for axios and rendering.
await flushPromises();
@@ -60,19 +60,9 @@ describe("JobMetrics/JobMetrics.vue", () => {
// Three metrics, begin metrics for two plugins
const metricsTables = wrapper.findAll(".metrics_plugin");
expect(metricsTables.length).to.equals(2);
expect(
metricsTables
.at(0)
.find(".metrics_plugin_title")
.text()
).to.equals("core");
expect(metricsTables.at(0).find(".metrics_plugin_title").text()).to.equals("core");
expect(metricsTables.at(0).findAll("tr").length).to.equals(2);
expect(
metricsTables
.at(1)
.find(".metrics_plugin_title")
.text()
).to.equals("extended");
expect(metricsTables.at(1).find(".metrics_plugin_title").text()).to.equals("extended");
expect(metricsTables.at(1).findAll("tr").length).to.equals(1);
});
});
@@ -22,24 +22,24 @@ import { mapGetters } from "vuex";
export default {
props: {
jobId: {
type: String
type: String,
},
datasetId: {
type: String
type: String,
},
datasetType: {
type: String,
default: "hda"
default: "hda",
},
includeTitle: {
type: Boolean,
default: true
}
default: true,
},
},
data() {
return {};
},
created: function() {
created: function () {
if (this.jobId) {
this.fetchJobMetricsForJobId(this.jobId);
} else {
@@ -48,17 +48,17 @@ export default {
},
computed: {
...mapGetters(["getJobMetricsByDatasetId", "getJobMetricsByJobId"]),
jobMetrics: function() {
jobMetrics: function () {
if (this.jobId) {
return this.getJobMetricsByJobId(this.jobId);
} else {
return this.getJobMetricsByDatasetId(this.datasetId, this.datasetType);
}
},
metricsByPlugins: function() {
metricsByPlugins: function () {
const metricsByPlugins = {};
const metrics = this.jobMetrics;
metrics.forEach(metric => {
metrics.forEach((metric) => {
if (!(metric.plugin in metricsByPlugins)) {
metricsByPlugins[metric.plugin] = {};
}
@@ -67,12 +67,12 @@ export default {
});
return metricsByPlugins;
},
orderedPlugins: function() {
orderedPlugins: function () {
return Object.keys(this.metricsByPlugins).sort();
}
},
},
methods: {
...mapCacheActions(["fetchJobMetricsForDatasetId", "fetchJobMetricsForJobId"])
}
...mapCacheActions(["fetchJobMetricsForDatasetId", "fetchJobMetricsForJobId"]),
},
};
</script>
@@ -53,27 +53,27 @@ Vue.use(BootstrapVue);
export default {
props: {
jobId: {
type: String
type: String,
},
datasetId: {
type: String
type: String,
},
datasetType: {
type: String,
default: "hda"
default: "hda",
},
includeTitle: {
type: Boolean,
default: true
}
default: true,
},
},
data() {
return {
parameters: [],
hasParameterErrors: false
hasParameterErrors: false,
};
},
created: function() {
created: function () {
let url;
if (this.jobId) {
url = `${getAppRoot()}api/jobs/${this.jobId}/parameters_display`;
@@ -83,31 +83,31 @@ export default {
this.ajaxCall(url);
},
computed: {
anyNotes: function() {
anyNotes: function () {
let hasNotes = false;
this.parameters.forEach(parameter => {
this.parameters.forEach((parameter) => {
hasNotes = hasNotes || parameter.notes;
});
return hasNotes;
}
},
},
methods: {
appRoot: function() {
appRoot: function () {
return getAppRoot();
},
ajaxCall: function(url) {
ajaxCall: function (url) {
axios
.get(url)
.then(response => response.data)
.then(data => {
.then((response) => response.data)
.then((data) => {
this.hasParameterErrors = data.has_parameter_errors;
this.parameters = data.parameters;
})
.catch(e => {
.catch((e) => {
console.error(e);
});
}
}
},
},
};
</script>
<style scoped>
@@ -28,10 +28,10 @@ import ProgressBar from "components/ProgressBar";
export default {
props: {
collection: { type: Object, required: true }, // backbone model
jobStatesSummary: { required: true }
jobStatesSummary: { required: true },
},
components: {
ProgressBar
ProgressBar,
},
mixins: [mixin],
computed: {
@@ -54,7 +54,7 @@ export default {
var jobCount = this.jobCount;
var errorCount = this.jobStatesSummary.numInError();
return `a ${this.collectionTypeDescription} with ${errorCount} / ${jobCount} jobs in error`;
}
}
},
},
};
</script>
@@ -37,7 +37,7 @@ export default {
},
otherPercent() {
return 1.0 - this.okPercent - this.runningPercent - this.errorPercent;
}
},
},
methods: {
countStates(states) {
@@ -48,6 +48,6 @@ export default {
}
}
return count;
}
}
},
},
};
@@ -10,17 +10,17 @@ export default {
classes: {
type: String,
required: false,
default: null
default: null,
},
message: {
type: String,
required: true
}
required: true,
},
},
computed: {
spinnerClasses() {
return ["loading-icon", "fa", "fa-spinner", "fa-spin"].concat(this.classes ? this.classes.split(" ") : []);
}
}
},
},
};
</script>
@@ -113,13 +113,13 @@ const RENDER_FUNCTIONS = {
job_parameters: (action, args, content) => {
const jobId = args.job_id;
return `<div class="job-parameters" job_id="${jobId}"></div>`;
}
},
};
md.renderer.rules.fence = function(tokens, idx, options, env, slf) {
const token = tokens[idx],
info = token.info ? token.info.trim() : "",
content = token.content;
md.renderer.rules.fence = function (tokens, idx, options, env, slf) {
const token = tokens[idx];
const info = token.info ? token.info.trim() : "";
const content = token.content;
if (info == "galaxy") {
const arr = FUNCTION_CALL_LINE_TEMPLATE.exec(content);
const action = arr[1];
@@ -142,25 +142,23 @@ function render_fenced_output(tag, objects, idAttr, metadataKey) {
$("." + tag).each((i, el) => {
const objectId = $(el).attr(idAttr);
const meta = objects[objectId][metadataKey];
$(el)
.find("code")
.text(meta);
$(el).find("code").text(meta);
});
}
export default {
props: {
markdownConfig: {
type: Object
type: Object,
},
readOnly: {
type: Boolean,
default: true
default: true,
},
exportLink: {
type: String,
required: false
}
required: false,
},
},
data() {
return {
@@ -168,17 +166,17 @@ export default {
historyDatasets: {},
historyDatasetCollections: {},
workflows: {},
jobs: {}
jobs: {},
};
},
computed: {
effectiveExportLink() {
const Galaxy = getGalaxyInstance();
return Galaxy.config.enable_beta_markdown_export ? this.exportLink : null;
}
},
},
watch: {
markdownConfig: function(mConfig, oldVal) {
markdownConfig: function (mConfig, oldVal) {
const markdown = mConfig.markdown;
this.markdownRendered = md.render(markdown);
this.historyDatasets = mConfig.history_datasets || {};
@@ -222,12 +220,12 @@ export default {
el: $(el),
linkTarget: "galaxy_main",
purgeAllowed: Galaxy.config.allow_user_dataset_purge,
logger: Galaxy.logger
logger: Galaxy.logger,
}).render(0);
});
});
}
}
},
},
};
</script>
@@ -1,5 +1,5 @@
<template>
<div style="display: flex; flex: 1; flex-direction: column">
<div style="display: flex; flex: 1; flex-direction: column;">
<ul class="galaxymark-toolbar" ref="menu" v-if="toolbar">
<li>
<a
@@ -87,28 +87,28 @@ export default {
props: {
initialMarkdown: {
required: true,
type: String
type: String,
},
toolbar: {
type: Boolean,
default: false
default: false,
},
showMarkdownHelp: {
// only used if toolbar is True - waiting on a general toolbar refactor
type: Function,
required: false
required: false,
},
onupdate: {
type: Function
}
type: Function,
},
},
data: function() {
data: function () {
return {
input: this.initialMarkdown
input: this.initialMarkdown,
};
},
methods: {
update: _.debounce(function(e) {
update: _.debounce(function (e) {
if (this.onupdate) {
this.onupdate(this.input);
}
@@ -126,14 +126,14 @@ export default {
},
_selectDataset(galaxyCall) {
dialog(
response => {
(response) => {
const datasetId = response.id;
this.insertGalaxyMarkdownBlock(`${galaxyCall}(history_dataset_id=${datasetId})`);
},
{
multiple: false,
format: null,
library: false // TODO: support?
library: false, // TODO: support?
}
);
},
@@ -144,18 +144,18 @@ export default {
this._selectDataset("history_dataset_as_image");
},
selectDatasetCollection() {
datasetCollectionDialog(response => {
datasetCollectionDialog((response) => {
this.insertGalaxyMarkdownBlock(
`history_dataset_collection_display(history_dataset_collection_id=${response.id})`
);
}, {});
},
selectWorkflow() {
workflowDialog(response => {
workflowDialog((response) => {
this.insertGalaxyMarkdownBlock(`workflow_display(workflow_id=${response.id})`);
}, {});
}
}
},
},
};
</script>
@@ -7,15 +7,15 @@ export default {
mask: {
type: String,
required: false,
default: ""
}
default: "",
},
},
computed: {
masker() {
return createMask({
mask: this.mask
mask: this.mask,
});
}
},
},
methods: {
getFormatted(value) {
@@ -24,6 +24,6 @@ export default {
return this.masker.resolve(result);
}
return result;
}
}
},
},
};
@@ -32,8 +32,8 @@ export default {
// bolt the rendered DOM onto the vue component's element
const backboneDom = galaxy.masthead.render();
this.$el.appendChild(backboneDom.$el[0]);
}
}
},
},
};
</script>
@@ -11,6 +11,6 @@ export function initMasthead(config, container) {
return new Component({
props: Object.keys(config),
propsData: config,
el: container
el: container,
});
}
+4 -4
View File
@@ -8,12 +8,12 @@ export default {
props: {
message: {
type: String,
required: true
required: true,
},
status: {
type: String,
default: "done"
}
}
default: "done",
},
},
};
</script>
@@ -7,7 +7,7 @@ import { keyedColorScheme } from "utils/color";
export default {
props: {
tag: { type: String, required: true }
tag: { type: String, required: true },
},
computed: {
tagLabel() {
@@ -18,10 +18,10 @@ export default {
return {
"background-color": primary,
color: contrasting,
"border-color": darker
"border-color": darker,
};
}
}
},
},
};
</script>
@@ -8,27 +8,27 @@ import { mapActions } from "vuex";
export default {
components: {
Nametag
Nametag,
},
props: {
storeKey: { type: String, required: true },
tags: { type: Array, required: false, default: () => [] }
tags: { type: Array, required: false, default: () => [] },
},
computed: {
// only display tags that start with name:
nameTags() {
return this.$store.getters.getTagsById(this.storeKey).filter(tag => tag.startsWith("name:"));
return this.$store.getters.getTagsById(this.storeKey).filter((tag) => tag.startsWith("name:"));
},
title() {
return `${this.nameTags.length} nametags`;
}
},
},
methods: {
...mapActions(["updateTags", "initializeTags"])
...mapActions(["updateTags", "initializeTags"]),
},
mounted() {
this.initializeTags({ key: this.storeKey, tags: this.tags });
}
},
};
</script>
@@ -9,41 +9,41 @@ import Markdown from "components/Markdown/Markdown.vue";
export default {
components: {
Markdown
Markdown,
},
props: {
pageId: {
type: String,
required: true
}
required: true,
},
},
computed: {
dataUrl: function() {
dataUrl: function () {
return getAppRoot() + `api/pages/${this.pageId}`;
},
exportUrl: function() {
exportUrl: function () {
return this.dataUrl + ".pdf";
}
},
},
data() {
return {
markdownConfig: {}
markdownConfig: {},
};
},
created: function() {
created: function () {
this.ajaxCall();
},
methods: {
ajaxCall: function() {
ajaxCall: function () {
axios
.get(this.dataUrl)
.then(response => {
.then((response) => {
this.markdownConfig = { ...response.data, markdown: response.data.content };
})
.catch(e => {
.catch((e) => {
console.error(e);
});
}
}
},
},
};
</script>
@@ -36,29 +36,29 @@ export default {
props: {
pageId: {
required: true,
type: String
type: String,
},
publicUrl: {
required: true,
type: String
type: String,
},
content: {
type: String
type: String,
},
contentFormat: {
type: String,
default: "html"
default: "html",
},
title: {
type: String
}
type: String,
},
},
computed: {},
methods: {
saveContent: function() {
saveContent: function () {
this.$refs.contentEditor.saveContent();
},
viewContent: function() {
viewContent: function () {
// TODO: Future iteration, contentEditor should have an entry point that can decide
// if it has changed content.
const r = window.confirm(
@@ -67,11 +67,11 @@ export default {
if (r === true) {
window.location = `${getAppRoot()}${this.publicUrl}`;
}
}
},
},
components: {
PageEditorHtml,
PageEditorMarkdown
}
PageEditorMarkdown,
},
};
</script>
@@ -51,7 +51,7 @@ var CONTROLS = {
DIALOG_EMBED_DATASET: "embed_dataset",
DIALOG_EMBED_WORKFLOW: "embed_workflow",
DIALOG_EMBED_PAGE: "embed_page",
DIALOG_EMBED_VISUALIZATION: "embed_visualization"
DIALOG_EMBED_VISUALIZATION: "embed_visualization",
};
// Based on the dialog type, return a dictionary of information about an item
@@ -103,7 +103,7 @@ function get_item_info(dialog_type) {
plural: item_plural,
controller: item_controller,
iclass: item_class,
list_ajax_url: ajax_url
list_ajax_url: ajax_url,
};
}
@@ -114,14 +114,14 @@ function make_item_importable(item_controller, item_id, item_type) {
type: "POST",
url: ajax_url,
data: { id: item_id, accessible: "True" },
error: function() {
error: function () {
alert(`Making ${item_type} accessible failed`);
}
},
});
}
// Completely replace WYM's dialog handling
WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtml) {
WYMeditor.editor.prototype.dialog = function (dialogType, dialogFeatures, bodyHtml) {
var wym = this;
var sStamp = wym.uniqueStamp();
var selected = wym.selected();
@@ -129,7 +129,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
// Swap out URL attribute for id/name attribute in link creation to enable anchor creation in page.
function set_link_id() {
// When "set link id" link clicked, update UI.
$("#set_link_id").click(function() {
$("#set_link_id").click(function () {
// Set label.
$("#link_attribute_label").text("ID/Name");
@@ -166,7 +166,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
"Create Link",
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='javascript:void(0)' role="button" id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${curURL}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${curTitle}' size='40' /></div><div>`,
{
"Make link": function() {
"Make link": function () {
// Get URL, name/title.
var sUrl = $(wym._options.hrefSelector).val() || "";
@@ -179,9 +179,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
// Set link attributes.
var link = $(`a[href=${sStamp}]`, wym._doc.body);
link.attr(WYMeditor.HREF, sUrl)
.attr(WYMeditor.TITLE, sName)
.attr("id", sId);
link.attr(WYMeditor.HREF, sUrl).attr(WYMeditor.TITLE, sName).attr("id", sId);
// If link's text is default (wym-...), change it to the title.
if (link.text().indexOf("wym-") === 0) {
@@ -190,9 +188,9 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
}
hide_modal();
},
Cancel: function() {
Cancel: function () {
hide_modal();
}
},
},
{},
set_link_id
@@ -227,7 +225,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
"<input type='text' class='wym_title' value='' size='40' />" +
"</div>",
{
Insert: function() {
Insert: function () {
var sUrl = $(wym._options.srcSelector).val();
if (sUrl.length > 0) {
wym._exec(WYMeditor.INSERT_IMAGE, sStamp);
@@ -238,9 +236,9 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
}
hide_modal();
},
Cancel: function() {
Cancel: function () {
hide_modal();
}
},
}
);
return;
@@ -267,7 +265,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
"<input type='text' class='wym_cols' value='2' size='3' />" +
"</div>",
{
Insert: function() {
Insert: function () {
var iRows = $(wym._options.rowsSelector).val();
var iCols = $(wym._options.colsSelector).val();
@@ -299,9 +297,9 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
}
hide_modal();
},
Cancel: function() {
Cancel: function () {
hide_modal();
}
},
}
);
}
@@ -336,7 +334,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
var grid = new GridView({
url_base: item_info.list_ajax_url,
embedded: true
embedded: true,
});
const Galaxy = getGalaxyInstance();
Galaxy.modal.show({
@@ -352,13 +350,13 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
),
closing_events: true,
buttons: {
Insert: function() {
Insert: function () {
// Make selected items accessible (importable) ?
var make_importable = false;
if ($("#make-importable:checked").val() != null) make_importable = true;
// Insert links to history for each checked item.
grid.$("input[name=id]:checked").each(function() {
grid.$("input[name=id]:checked").each(function () {
var item_id = $(this).val();
// Make item importable?
@@ -367,7 +365,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
// Insert link(s) to item(s). This is done by getting item info and then manipulating wym.
var url_template = configs.get_name_and_link_url + item_id;
var ajax_url = url_template.replace("ITEM_CONTROLLER", item_info.controller);
$.getJSON(ajax_url, returned_item_info => {
$.getJSON(ajax_url, (returned_item_info) => {
// Get link text.
wym._exec(WYMeditor.CREATE_LINK, sStamp);
var link_text = $(`a[href=${sStamp}]`, wym._doc.body).text();
@@ -391,10 +389,10 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
});
Galaxy.modal.hide();
},
Close: function() {
Close: function () {
Galaxy.modal.hide();
}
}
},
},
});
}
// EMBED GALAXY OBJECT DIALOGS
@@ -427,7 +425,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
const grid = new GridView({
url_base: item_info.list_ajax_url,
embedded: true
embedded: true,
});
const Galaxy = getGalaxyInstance();
Galaxy.modal.show({
@@ -444,21 +442,17 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
closing_events: true,
extra_class: "pages-embed",
buttons: {
Embed: function() {
Embed: function () {
// Make selected items accessible (importable) ?
var make_importable = false;
if ($("#make-importable:checked").val() != null) make_importable = true;
grid.$("input[name=id]:checked").each(function() {
grid.$("input[name=id]:checked").each(function () {
var elt = $(this);
// Get item ID and name.
var item_id = elt.val();
// Use ':first' because there are many labels in table; the first one is the item name.
var item_name = elt
.closest("td")
.next("td")
.find("label")
.text();
var item_name = elt.closest("td").next("td").find("label").text();
if (make_importable) make_item_importable(item_info.controller, item_id, item_info.singular);
@@ -484,7 +478,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
item_info.singular.toLowerCase(),
" when it is displayed.]",
"</p>",
"</div>"
"</div>",
].join("");
// Insert embedded item into document.
@@ -492,10 +486,10 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
});
Galaxy.modal.hide();
},
Close: function() {
Close: function () {
Galaxy.modal.hide();
}
}
},
},
});
}
};
@@ -509,7 +503,7 @@ function renderEditorWithContent(pageId, content) {
set_accessible_url: `${appRoot}ITEM_CONTROLLER/set_accessible_async`,
get_name_and_link_url: `${appRoot}ITEM_CONTROLLER/get_name_and_link_async?id=`,
editor_base_path: `${appRoot}static/wymeditor/`,
iframe_base_path: `${appRoot}static/wymeditor/iframe/galaxy/`
iframe_base_path: `${appRoot}static/wymeditor/iframe/galaxy/`,
};
setPageConfigs(pageConfigs);
@@ -540,22 +534,22 @@ function renderEditorWithContent(pageId, content) {
{
name: "Superscript",
title: _l("Superscript"),
css: "wym_tools_superscript"
css: "wym_tools_superscript",
},
{
name: "Subscript",
title: _l("Subscript"),
css: "wym_tools_subscript"
css: "wym_tools_subscript",
},
{
name: "InsertOrderedList",
title: "Ordered_List",
css: "wym_tools_ordered_list"
css: "wym_tools_ordered_list",
},
{
name: "InsertUnorderedList",
title: "Unordered_List",
css: "wym_tools_unordered_list"
css: "wym_tools_unordered_list",
},
{ name: "Indent", title: "Indent", css: "wym_tools_indent" },
{ name: "Outdent", title: "Outdent", css: "wym_tools_outdent" },
@@ -564,8 +558,8 @@ function renderEditorWithContent(pageId, content) {
{ name: "CreateLink", title: "Link", css: "wym_tools_link" },
{ name: "Unlink", title: "Unlink", css: "wym_tools_unlink" },
{ name: "InsertImage", title: "Image", css: "wym_tools_image" },
{ name: "InsertTable", title: "Table", css: "wym_tools_table" }
]
{ name: "InsertTable", title: "Table", css: "wym_tools_table" },
],
});
// Get the editor object
editor = $.wymeditors(0);
@@ -600,21 +594,21 @@ function renderEditorWithContent(pageId, content) {
// Add menu options.
make_popupmenu(insert_link_menu_button, {
"Insert History Link": function() {
"Insert History Link": function () {
editor.dialog(CONTROLS.DIALOG_HISTORY_LINK);
},
"Insert Dataset Link": function() {
"Insert Dataset Link": function () {
editor.dialog(CONTROLS.DIALOG_DATASET_LINK);
},
"Insert Workflow Link": function() {
"Insert Workflow Link": function () {
editor.dialog(CONTROLS.DIALOG_WORKFLOW_LINK);
},
"Insert Page Link": function() {
"Insert Page Link": function () {
editor.dialog(CONTROLS.DIALOG_PAGE_LINK);
},
"Insert Visualization Link": function() {
"Insert Visualization Link": function () {
editor.dialog(CONTROLS.DIALOG_VISUALIZATION_LINK);
}
},
});
//
@@ -629,18 +623,18 @@ function renderEditorWithContent(pageId, content) {
// Add menu options.
make_popupmenu(embed_object_button, {
"Embed History": function() {
"Embed History": function () {
editor.dialog(CONTROLS.DIALOG_EMBED_HISTORY);
},
"Embed Dataset": function() {
"Embed Dataset": function () {
editor.dialog(CONTROLS.DIALOG_EMBED_DATASET);
},
"Embed Workflow": function() {
"Embed Workflow": function () {
editor.dialog(CONTROLS.DIALOG_EMBED_WORKFLOW);
},
"Embed Visualization": function() {
"Embed Visualization": function () {
editor.dialog(CONTROLS.DIALOG_EMBED_VISUALIZATION);
}
},
//"Embed Page": function() {
// editor.dialog(CONTROLS.DIALOG_EMBED_PAGE);
//}
@@ -651,22 +645,22 @@ export default {
props: {
pageId: {
required: true,
type: String
type: String,
},
content: {
type: String
}
type: String,
},
},
created: function() {
created: function () {
this.$nextTick(() => {
renderEditorWithContent(this.pageId, this.content);
});
},
methods: {
saveContent: function() {
saveContent: function () {
save(this.pageId, editor.xhtml());
}
}
},
},
};
</script>
@@ -18,31 +18,31 @@ import { save } from "./util";
export default {
components: {
MarkdownEditor
MarkdownEditor,
},
data: function() {
data: function () {
return {
content: "",
showMarkdownHelp: showMarkdownHelp
showMarkdownHelp: showMarkdownHelp,
};
},
props: {
pageId: {
required: true,
type: String
type: String,
},
initialContent: {
type: String
}
type: String,
},
},
methods: {
onUpdate: function(content) {
onUpdate: function (content) {
this.content = content;
},
saveContent: function() {
saveContent: function () {
save(this.pageId, this.content);
}
}
},
},
};
</script>
@@ -82,6 +82,6 @@ import { hide_modal, show_modal } from "layout/modal";
export function showMarkdownHelp() {
const markdownHelpBody = $(markdownHelp);
show_modal("Pages Markdown Help", markdownHelpBody, {
Ok: hide_modal
Ok: hide_modal,
});
}
@@ -11,5 +11,5 @@ export const save = (pageId, content) => {
axios
.post(`${getAppRoot()}api/pages/${pageId}/revisions`, { content: content })
.finally(hide_modal)
.catch(error_message => Toast.error("Failed to save page: " + errorMessageAsString(error_message)));
.catch((error_message) => Toast.error("Failed to save page: " + errorMessageAsString(error_message)));
};
@@ -24,16 +24,16 @@ export default {
components: { FontAwesomeIcon },
data() {
return {
tooltipText: "Show favorites"
tooltipText: "Show favorites",
};
},
directives: {
"v-b-tooltip": VBTooltip
"v-b-tooltip": VBTooltip,
},
methods: {
onFavorites() {
this.$emit("onFavorites", "#favorites");
}
}
},
},
};
</script>
@@ -5,7 +5,7 @@
class="progress-bar progress-bar-notransition"
:class="`progress-bar-${status}`"
:style="{
width: `${percentage}%`
width: `${percentage}%`,
}"
></div>
<a
@@ -37,12 +37,12 @@ export default {
name: "UploadButton",
components: { FontAwesomeIcon },
directives: {
"v-b-tooltip": VBTooltip
"v-b-tooltip": VBTooltip,
},
data() {
return {
status: "",
percentage: 0
percentage: 0,
};
},
methods: {
@@ -50,7 +50,7 @@ export default {
const Galaxy = getGalaxyInstance();
e.preventDefault();
Galaxy.upload.show();
}
},
},
created() {
const Galaxy = getGalaxyInstance();
@@ -59,7 +59,7 @@ export default {
this.status = Galaxy.upload.model.attributes.status;
this.percentage = Galaxy.upload.model.attributes.percentage;
});
}
},
};
</script>
@@ -5,8 +5,8 @@ describe("Tool", () => {
it("test tool", () => {
const wrapper = mount(Tool, {
propsData: {
tool: {}
}
tool: {},
},
});
const nameElement = wrapper.findAll(".name");
expect(nameElement.at(0).text()).to.equal("");
@@ -23,11 +23,11 @@ describe("Tool", () => {
const wrapper = mount(Tool, {
propsData: {
tool: {
name: "name"
name: "name",
},
operationIcon: "operationIconClass",
operationTitle: "operationTitle"
}
operationTitle: "operationTitle",
},
});
const nameElement = wrapper.findAll(".name");
expect(nameElement.at(0).text()).to.equal("name");
@@ -41,10 +41,10 @@ describe("Tool", () => {
propsData: {
tool: {
name: "name",
description: "description"
description: "description",
},
hideName: true
}
hideName: true,
},
});
const nameElement = wrapper.findAll(".name");
expect(nameElement.length).to.equal(0);
@@ -38,20 +38,20 @@ export default {
props: {
tool: {
type: Object,
required: true
required: true,
},
operationTitle: {
type: String
type: String,
},
operationIcon: {
type: String
type: String,
},
hideName: {
type: Boolean
type: Boolean,
},
toolKey: {
type: String
}
type: String,
},
},
computed: {
targetClass() {
@@ -60,7 +60,7 @@ export default {
} else {
return null;
}
}
},
},
methods: {
onClick(evt) {
@@ -70,7 +70,7 @@ export default {
onOperation(evt) {
ariaAlert(`${this.tool.name} operation selected from panel`);
this.$emit("onOperation", this.tool, evt);
}
}
},
},
};
</script>
@@ -11,28 +11,28 @@ import DelayedInput from "components/Common/DelayedInput";
export default {
name: "ToolSearch",
components: {
DelayedInput
DelayedInput,
},
props: {
query: {
type: String
type: String,
},
placeholder: {
type: String
}
type: String,
},
},
data() {
return {
favorites: ["#favs", "#favorites", "#favourites"],
minQueryLength: 3,
loading: false
loading: false,
};
},
computed: {
favoritesResults() {
const Galaxy = getGalaxyInstance();
return Galaxy.user.getFavorites().tools;
}
},
},
methods: {
checkQuery(q) {
@@ -44,13 +44,13 @@ export default {
this.loading = true;
axios
.get(`${getAppRoot()}api/tools`, {
params: { q }
params: { q },
})
.then(response => {
.then((response) => {
this.loading = false;
this.$emit("onResults", response.data);
})
.catch(err => {
.catch((err) => {
this.loading = false;
this.$emit("onError", err);
});
@@ -58,7 +58,7 @@ export default {
} else {
this.$emit("onResults", null);
}
}
}
},
},
};
</script>
@@ -7,9 +7,9 @@ describe("ToolSection", () => {
const wrapper = mount(ToolSection, {
propsData: {
category: {
name: "name"
}
}
name: "name",
},
},
});
const nameElement = wrapper.findAll(".name");
expect(nameElement.at(0).text()).to.equal("name");
@@ -24,14 +24,14 @@ describe("ToolSection", () => {
title: "tool_section",
elems: [
{
name: "name"
name: "name",
},
{
text: "text"
}
]
}
}
text: "text",
},
],
},
},
});
expect(wrapper.vm.opened).to.equal(false);
const $sectionName = wrapper.find(".name");
@@ -54,44 +54,37 @@ describe("ToolSection", () => {
title: "tool_section",
elems: [
{
name: "name"
name: "name",
},
{
text: "text"
}
]
text: "text",
},
],
},
queryFilter: "test"
}
queryFilter: "test",
},
});
expect(wrapper.vm.opened).to.equal(true);
const $sectionName = wrapper.find(".name");
$sectionName.trigger("click");
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(false);
wrapper.vm.queryFilter = "";
wrapper.setProps({ queryFilter: "" });
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(false);
wrapper.vm.queryFilter = "test";
wrapper.setProps({ queryFilter: "test" });
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(true);
wrapper.vm.disableFilter = true;
wrapper.setProps({ disableFilter: true });
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(true);
wrapper.vm.queryFilter = "";
wrapper.setProps({ queryFilter: "" });
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(false);
$sectionName.trigger("click");
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(true);
wrapper.vm.queryFilter = "test";
wrapper.setProps({ queryFilter: "test" });
await Vue.nextTick();
expect(wrapper.vm.opened).to.equal(false);
});
@@ -56,45 +56,45 @@ import ariaAlert from "utils/ariaAlert";
export default {
name: "ToolSection",
components: {
Tool
Tool,
},
props: {
category: {
type: Object,
required: true
required: true,
},
queryFilter: {
type: String
type: String,
},
disableFilter: {
type: Boolean
type: Boolean,
},
hideName: {
type: Boolean
type: Boolean,
},
operationTitle: {
type: String
type: String,
},
operationIcon: {
type: String
type: String,
},
toolKey: {
type: String
type: String,
},
sectionName: {
type: String,
default: "default"
}
default: "default",
},
},
data() {
return {
opened: this.checkFilter()
opened: this.checkFilter(),
};
},
watch: {
queryFilter() {
this.opened = this.checkFilter();
}
},
},
computed: {
name() {
@@ -105,7 +105,7 @@ export default {
},
hasElements() {
return this.category.elems && this.category.elems.length > 0;
}
},
},
methods: {
checkFilter() {
@@ -121,8 +121,8 @@ export default {
this.opened = !this.opened;
const currentState = this.opened ? "opened" : "closed";
ariaAlert(`${this.name} tools menu ${currentState}`);
}
}
},
},
};
</script>
@@ -1,13 +1,15 @@
<template>
<div :id="side" class="unified-panel-outer-wrap" :style="styles">
<component :is="currentPanel" v-bind="currentPanelProperties" />
<slot name="panel">
<component :is="currentPanel" v-bind="currentPanelProperties" />
</slot>
<div class="unified-panel-footer">
<div
class="panel-collapse"
:class="{
left: side === 'left',
right: side === 'right',
hidden: !show
hidden: !show,
}"
@click="toggle"
/>
@@ -26,7 +28,7 @@ export default {
data() {
return {
show: true,
width: 288
width: 288,
};
},
computed: {
@@ -35,7 +37,7 @@ export default {
styles[this.side] = this.show ? "0" : `-${this.width}px`;
styles["width"] = this.width + "px";
return styles;
}
},
},
methods: {
dragHandler(e) {
@@ -82,8 +84,16 @@ export default {
document.getElementById("center").style.transition = "";
}, 250);
}
}
}
},
hide() {
this.show = false;
document.getElementById("center").style.transition = `${this.side} 200ms linear`;
document.getElementById("center").style[this.side] = "0";
setTimeout(() => {
document.getElementById("center").style.transition = "";
}, 250);
},
},
};
</script>
@@ -55,28 +55,28 @@ export default {
UploadButton,
FavoritesButton,
ToolSection,
ToolSearch
ToolSearch,
},
data() {
return {
query: null,
results: null,
workflow: null
workflow: null,
};
},
props: {
toolbox: {
type: Array,
required: true
required: true,
},
stored_workflow_menu_entries: {
type: Array,
required: true
required: true,
},
workflowTitle: {
type: String,
default: _l("Workflows")
}
default: _l("Workflows"),
},
},
computed: {
categories() {
@@ -91,17 +91,17 @@ export default {
{
title: _l("All workflows"),
href: `${getAppRoot()}workflows/list`,
id: "list"
id: "list",
},
...this.stored_workflow_menu_entries.map(menuEntry => {
...this.stored_workflow_menu_entries.map((menuEntry) => {
return {
id: menuEntry.id,
title: menuEntry.name,
href: `${getAppRoot()}workflows/run?id=${menuEntry.id}`
href: `${getAppRoot()}workflows/run?id=${menuEntry.id}`,
};
})
}),
];
}
},
},
methods: {
onQuery(query) {
@@ -122,10 +122,10 @@ export default {
evt.preventDefault();
Galaxy.router.push("/", {
tool_id: tool.id,
version: tool.version
version: tool.version,
});
}
}
}
},
},
};
</script>
@@ -22,8 +22,8 @@
@onClick="onInsertModule"
/>
<tool-section
:category="dataManagers"
:key="dataManagers.id"
:category="dataManagerSection"
:key="dataManagerSection.id"
:query-filter="query"
:disable-filter="true"
@onClick="onInsertTool"
@@ -40,7 +40,7 @@
<tool-section
:category="workflowSection"
:key="workflowSection.name"
operation-icon="fa fa-copy"
operation-icon="fa fa-files-o"
operation-title="Insert individual steps."
:query-filter="query"
:disable-filter="true"
@@ -53,6 +53,7 @@
</template>
<script>
import _l from "utils/localization";
import ToolSection from "./Common/ToolSection";
import ToolSearch from "./Common/ToolSearch";
import { filterToolSections } from "./utilities";
@@ -61,49 +62,61 @@ export default {
name: "ToolBox",
components: {
ToolSection,
ToolSearch
ToolSearch,
},
data() {
return {
query: null,
results: null
results: null,
};
},
props: {
toolbox: {
type: Array,
required: true
required: true,
},
workflowSection: {
type: Object
},
workflowGlobals: {
type: Object
workflows: {
type: Array,
required: true,
},
dataManagers: {
type: Object
type: Array,
required: true,
},
moduleSections: {
type: Array
}
type: Array,
required: true,
},
},
computed: {
workflowSection() {
return {
name: _l("Workflows"),
elems: this.workflows,
};
},
dataManagerSection() {
return {
name: _l("Data Managers"),
elems: this.dataManagers,
};
},
categories() {
return filterToolSections(this.toolsLayout, this.results);
},
toolsLayout() {
return this.toolbox.map(section => {
return this.toolbox.map((section) => {
return {
...section,
elems:
section.elems &&
section.elems.map(el => {
section.elems.map((el) => {
el.disabled = !el.is_workflow_compatible;
return el;
})
}),
};
});
}
},
},
methods: {
onQuery(query) {
@@ -114,19 +127,19 @@ export default {
},
onInsertTool(tool, evt) {
evt.preventDefault();
this.workflowGlobals.app.add_node_for_tool(tool.id, tool.name);
this.$emit("onInsertTool", tool.id, tool.name);
},
onInsertModule(module, evt) {
evt.preventDefault();
this.workflowGlobals.app.add_node_for_module(module.name, module.title);
this.$emit("onInsertModule", module.name, module.title);
},
onInsertWorkflow(workflow, evt) {
evt.preventDefault();
this.workflowGlobals.app.add_node_for_subworkflow(workflow.latest_id, workflow.name);
this.$emit("onInsertWorkflow", workflow.latest_id, workflow.name);
},
onInsertWorkflowSteps(workflow) {
this.workflowGlobals.app.copy_into_workflow(workflow.id, workflow.step_count);
}
}
this.$emit("onInsertWorkflowSteps", workflow.id, workflow.step_count);
},
},
};
</script>
@@ -1,31 +0,0 @@
/**
* Endpoint for mounting Panel components
*/
import Vue from "vue";
import _l from "utils/localization";
import SidePanel from "./SidePanel";
import ToolBoxWorkflow from "./ToolBoxWorkflow";
export const mountToolBoxWorkflow = options => {
const component = Vue.extend(SidePanel);
return new component({
propsData: {
side: "left",
currentPanel: ToolBoxWorkflow,
currentPanelProperties: {
toolbox: options.toolbox,
workflowGlobals: options.workflow_globals,
moduleSections: options.module_sections,
dataManagers: {
name: _l("Data Managers"),
elems: options.data_managers
},
workflowSection: {
name: _l("Workflows"),
elems: options.workflows
}
}
},
el: "#left"
});
};
@@ -1,16 +1,16 @@
export function filterToolSections(layout, results) {
if (results) {
const filteredLayout = layout.map(category => {
const filteredLayout = layout.map((category) => {
return {
...category,
elems:
category.elems &&
category.elems.filter(el => {
category.elems.filter((el) => {
return !el.text && results.includes(el.id);
})
}),
};
});
return filteredLayout.filter(category => {
return filteredLayout.filter((category) => {
const isSection = category.elems && category.elems.length > 0;
const isMatchedTool = !category.text && results.includes(category.id);
return isSection || isMatchedTool;
+11 -11
View File
@@ -76,7 +76,7 @@ export default {
selected: null,
name: null,
error: null,
fixed: false
fixed: false,
};
},
created() {
@@ -90,15 +90,15 @@ export default {
}
axios
.get(url)
.then(response => {
.then((response) => {
this.plugins = response.data;
})
.catch(e => {
.catch((e) => {
this.error = this._errorMessage(e);
});
},
methods: {
select: function(plugin) {
select: function (plugin) {
if (this.fixed) {
this.create(plugin);
} else {
@@ -107,14 +107,14 @@ export default {
if (history_id) {
axios
.get(`${getAppRoot()}api/plugins/${plugin.name}?history_id=${history_id}`)
.then(response => {
.then((response) => {
this.name = plugin.name;
this.hdas = response.data && response.data.hdas;
if (this.hdas && this.hdas.length > 0) {
this.selected = this.hdas[0].id;
}
})
.catch(e => {
.catch((e) => {
this.error = this._errorMessage(e);
});
} else {
@@ -122,7 +122,7 @@ export default {
}
}
},
create: function(plugin) {
create: function (plugin) {
const href = `${plugin.href}?dataset_id=${this.selected}`;
if (plugin.target == "_top") {
window.location.href = href;
@@ -130,17 +130,17 @@ export default {
$("#galaxy_main").attr("src", href);
}
},
match: function(plugin) {
match: function (plugin) {
return (
!this.search ||
plugin.name.indexOf(this.search) != -1 ||
(plugin.description && plugin.description.indexOf(this.search) != -1)
);
},
_errorMessage: function(e) {
_errorMessage: function (e) {
const message = e && e.response && e.response.data && e.response.data.err_msg;
return message || "Request failed for an unknown reason.";
}
}
},
},
};
</script>
@@ -25,12 +25,12 @@ export default {
newProgress: { type: Number, default: 0.0 },
newMessage: { type: String, default: null },
errorProgress: { type: Number, default: 0.0 },
errorMessage: { type: String, default: null }
errorMessage: { type: String, default: null },
},
methods: {
styleFromProgress: function(progress) {
styleFromProgress: function (progress) {
return { width: `${progress * 100}%` };
}
}
},
},
};
</script>
@@ -40,7 +40,7 @@
:class="{
'rules-container-vertical': initialElements && vertical,
'rules-container-horizontal': initialElements && horizontal,
'rules-container-full': initialElements == null
'rules-container-full': initialElements == null,
}"
>
<rule-component rule-type="sort" :display-rule-type="displayRuleType" :builder="this">
@@ -351,8 +351,8 @@
:key="map.type"
@remove="removeMapping(index)"
@edit="displayRuleType = 'mapping'"
@mouseover.native="map.columns.forEach(col => highlightColumn(col))"
@mouseout.native="map.columns.forEach(col => unhighlightColumn(col))"
@mouseover.native="map.columns.forEach((col) => highlightColumn(col))"
@mouseout.native="map.columns.forEach((col) => unhighlightColumn(col))"
:col-headers="colHeaders"
/>
<div v-if="mapping.length == 0">
@@ -620,47 +620,47 @@ const ColumnSelector = {
</ol>
</div>
`,
data: function() {
data: function () {
return {
l: _l
l: _l,
};
},
props: {
target: {
required: true
required: true,
},
label: {
required: false,
type: String,
default: _l("From Column")
default: _l("From Column"),
},
help: {
required: false
required: false,
},
colHeaders: {
type: Array,
required: true
required: true,
},
multiple: {
type: Boolean,
required: false,
default: false
default: false,
},
ordered: {
type: Boolean,
required: false,
default: false
default: false,
},
valueAsList: {
type: Boolean,
required: false,
default: false
default: false,
},
orderedEdit: {
type: Boolean,
required: false,
default: false
}
default: false,
},
},
computed: {
remainingHeaders() {
@@ -678,13 +678,13 @@ const ColumnSelector = {
},
title() {
return _l("Select a column");
}
},
},
methods: {
handleInput(value) {
if (this.multiple) {
// https://stackoverflow.com/questions/262427/why-does-parseint-yield-nan-with-arraymap
const val = value.map(idx => parseInt(idx));
const val = value.map((idx) => parseInt(idx));
this.$emit("update:target", val);
} else {
let val = parseInt(value);
@@ -705,11 +705,11 @@ const ColumnSelector = {
const swapVal = this.target[value - 1];
Vue.set(this.target, value - 1, this.target[value]);
Vue.set(this.target, value, swapVal);
}
},
},
components: {
Select2
}
Select2,
},
};
const RegularExpressionInput = {
@@ -722,8 +722,8 @@ const RegularExpressionInput = {
`,
props: {
target: {
required: true
}
required: true,
},
},
computed: {
label() {
@@ -739,8 +739,8 @@ const RegularExpressionInput = {
return _l(
`Regular expressions are patterns used to match character combinations in strings. This input accepts Python-style regular expressions, find more information about these in <a href="https://pythonforbiologists.com/regular-expressions/">this Python for Biologists tutorial</a>.`
);
}
}
},
},
};
const RuleDisplay = {
@@ -762,12 +762,12 @@ const RuleDisplay = {
props: {
rule: {
required: true,
type: Object
type: Object,
},
colHeaders: {
type: Array,
required: true
}
required: true,
},
},
computed: {
title() {
@@ -779,7 +779,7 @@ const RuleDisplay = {
},
removeTitle() {
return _l("Remove this rule.");
}
},
},
methods: {
edit() {
@@ -787,8 +787,8 @@ const RuleDisplay = {
},
remove() {
this.$emit("remove");
}
}
},
},
};
const IdentifierDisplay = {
@@ -802,15 +802,15 @@ const IdentifierDisplay = {
props: {
type: {
type: String,
required: true
required: true,
},
columns: {
required: true
required: true,
},
colHeaders: {
type: Array,
required: true
}
required: true,
},
},
methods: {
remove() {
@@ -818,7 +818,7 @@ const IdentifierDisplay = {
},
edit() {
this.$emit("edit");
}
},
},
computed: {
typeDisplay() {
@@ -835,8 +835,8 @@ const IdentifierDisplay = {
},
columnsLabel() {
return RuleDefs.columnDisplay(this.columns, this.colHeaders);
}
}
},
},
};
const RuleTargetComponent = {
@@ -844,11 +844,11 @@ const RuleTargetComponent = {
props: {
ruleType: {
type: String,
required: true
required: true,
},
builder: {
required: true
}
required: true,
},
},
computed: {
linkClassName() {
@@ -856,8 +856,8 @@ const RuleTargetComponent = {
},
title() {
return RULES[this.ruleType].title;
}
}
},
},
};
const RuleComponent = {
@@ -869,23 +869,23 @@ const RuleComponent = {
<button type="button" class="btn btn-primary rule-editor-ok" @click="okay">{{ applyLabel }}</button>
</div>
</div>`,
data: function() {
data: function () {
return {
applyLabel: _l("Apply"),
cancelLabel: _l("Cancel")
cancelLabel: _l("Cancel"),
};
},
props: {
ruleType: {
type: String,
required: true
required: true,
},
displayRuleType: {
required: true
required: true,
},
builder: {
required: true
}
required: true,
},
},
methods: {
cancel() {
@@ -894,28 +894,28 @@ const RuleComponent = {
okay() {
this.builder.handleRuleSave(this.ruleType);
this.cancel();
}
},
},
computed: {
typeToClass() {
return "rule-edit-" + this.ruleType.replace(/_/g, "-");
}
}
},
},
};
const StateDiv = {
template: `
<div class="rule-collection-creator collection-creator flex-row-container">
<slot></slot>
</div>`
</div>`,
};
const RuleModalHeader = {
template: `<div class="header flex-row no-flex"><slot></slot></div>`
template: `<div class="header flex-row no-flex"><slot></slot></div>`,
};
const RuleModalMiddle = {
template: `<div class="middle flex-row flex-row-container"><slot></slot></div>`
template: `<div class="middle flex-row flex-row-container"><slot></slot></div>`,
};
const RuleModalFooter = {
@@ -927,13 +927,14 @@ const RuleModalFooter = {
<slot></slot>
</div>
</div>
</div>`
</div>`,
};
export default {
data: function() {
data: function () {
let orientation = "vertical";
let mapping, rules;
let mapping;
let rules;
if (this.initialRules) {
mapping = this.initialRules.mapping.slice();
rules = this.initialRules.rules.slice();
@@ -953,7 +954,7 @@ export default {
for (const index in collectionTypeRanks) {
rules.push({
type: "add_column_metadata",
value: "identifier" + index
value: "identifier" + index,
});
}
} else {
@@ -961,29 +962,29 @@ export default {
// just assume a list is given by default.
rules.push({
type: "add_column_metadata",
value: "identifier0"
value: "identifier0",
});
}
} else if (this.elementsType == "datasets") {
rules.push(
{
type: "add_column_metadata",
value: "hid"
value: "hid",
},
{
type: "add_column_metadata",
value: "name"
value: "name",
}
);
} else if (this.elementsType == "library_datasets") {
rules.push({
type: "add_column_metadata",
value: "name"
value: "name",
});
} else if (this.elementsType == "ftp") {
rules.push({
type: "add_column_metadata",
value: "path"
value: "path",
});
}
}
@@ -1064,58 +1065,58 @@ export default {
genome: null,
hideSourceItems: this.defaultHideSourceItems,
addNameTag: false,
orientation: orientation
orientation: orientation,
};
},
props: {
initialElements: {
required: true
required: true,
},
importType: {
type: String,
required: false,
default: "collections"
default: "collections",
},
elementsType: {
type: String,
required: false,
default: "datasets"
default: "datasets",
},
// required if elementsType is "datasets" - hook into Backbone code for creating
// collections from HDAs, etc...
creationFn: {
required: false,
type: Function
type: Function,
},
// required if elementsType is "collection_contents" - hook into tool form to update
// rule parameter
saveRulesFn: {
required: false,
type: Function
type: Function,
},
initialRules: {
required: false,
type: Object
type: Object,
},
defaultHideSourceItems: {
type: Boolean,
required: false,
default: true
default: true,
},
// Callbacks sent in by modal code.
oncancel: {
required: true,
type: Function
type: Function,
},
oncreate: {
required: true,
type: Function
type: Function,
},
ftpUploadSite: {
type: String,
required: false,
default: null
}
default: null,
},
},
computed: {
exisistingDatasets() {
@@ -1168,7 +1169,7 @@ export default {
}
},
hasActiveMappingEdit() {
const has = _.any(_.values(this.mapping), mapping => mapping.editing);
const has = _.any(_.values(this.mapping), (mapping) => mapping.editing);
return has;
},
activeRule() {
@@ -1236,7 +1237,7 @@ export default {
} else if (mappingDisplay.length > 1) {
formattedHeaders[colIndex] += ` (${[
mappingDisplay.slice(0, -1).join(", "),
mappingDisplay.slice(-1)[0]
mappingDisplay.slice(-1)[0],
].join(" & ")})`;
}
}
@@ -1291,7 +1292,7 @@ export default {
if (this.mappingAsDict.list_identifiers) {
identifierColumns = this.mappingAsDict.list_identifiers.columns;
}
let collectionType = identifierColumns.map(col => "list").join(":");
let collectionType = identifierColumns.map((col) => "list").join(":");
if (this.mappingAsDict.paired_identifier) {
if (collectionType) {
collectionType += ":paired";
@@ -1334,14 +1335,16 @@ export default {
return valid;
},
hotData() {
let data, sources, columns;
let data;
let sources;
let columns;
if (
this.elementsType == "datasets" ||
this.elementsType == "library_datasets" ||
this.elementsType == "ftp"
) {
sources = this.initialElements.slice();
data = sources.map(el => []);
data = sources.map((el) => []);
columns = [];
} else if (this.elementsType == "collection_contents") {
const collection = this.initialElements;
@@ -1360,7 +1363,7 @@ export default {
}
} else {
data = this.initialElements.slice();
sources = data.map(el => null);
sources = data.map((el) => null);
columns = [];
if (this.initialElements) {
this.initialElements[0].forEach(() => columns.push("new"));
@@ -1370,7 +1373,7 @@ export default {
},
colHeadersPerRule() {
return this.hotData.colHeadersPerRule;
}
},
},
methods: {
l(str) {
@@ -1413,7 +1416,7 @@ export default {
this.ruleView = "source";
},
resetSource() {
const replacer = function(key, value) {
const replacer = function (key, value) {
if (key == "error" || key == "warn") {
return undefined;
}
@@ -1421,7 +1424,7 @@ export default {
};
const asJson = {
rules: this.rules,
mapping: this.mapping
mapping: this.mapping,
};
if (!this.exisistingDatasets) {
if (this.extension !== UploadUtils.DEFAULT_EXTENSION) {
@@ -1494,7 +1497,7 @@ export default {
},
waitOnJob(response) {
const jobId = response.data.jobs[0].id;
const handleJobShow = jobResponse => {
const handleJobShow = (jobResponse) => {
const state = jobResponse.data.state;
this.waitingJobState = state;
if (JobStatesModel.NON_TERMINAL_STATES.indexOf(state) !== -1) {
@@ -1512,15 +1515,12 @@ export default {
}
};
const doJobCheck = () => {
axios
.get(`${getAppRoot()}api/jobs/${jobId}`)
.then(handleJobShow)
.catch(this.renderFetchError);
axios.get(`${getAppRoot()}api/jobs/${jobId}`).then(handleJobShow).catch(this.renderFetchError);
};
setTimeout(doJobCheck, 1000);
},
doFullJobCheck(jobId) {
const handleJobShow = jobResponse => {
const handleJobShow = (jobResponse) => {
const stderr = jobResponse.data.stderr;
if (stderr) {
let errorMessage = "An error was encountered while running your upload job. ";
@@ -1532,10 +1532,7 @@ export default {
this.errorMessage = errorMessage;
}
};
axios
.get(`${getAppRoot()}api/jobs/${jobId}?full=True`)
.then(handleJobShow)
.catch(this.renderFetchError);
axios.get(`${getAppRoot()}api/jobs/${jobId}?full=True`).then(handleJobShow).catch(this.renderFetchError);
},
renderFetchError(error) {
this.state = "error";
@@ -1551,17 +1548,17 @@ export default {
this.orientation = this.orientation == "horizontal" ? "vertical" : "horizontal";
const hotTable = this.$refs.hotTable.table;
if (this.orientation == "horizontal") {
this.$nextTick(function() {
this.$nextTick(function () {
const fullWidth = $(".rule-builder-body").width();
hotTable.updateSettings({
width: fullWidth
width: fullWidth,
});
});
} else {
this.$nextTick(function() {
this.$nextTick(function () {
const fullWidth = $(".rule-builder-body").width();
hotTable.updateSettings({
width: fullWidth - 270
width: fullWidth - 270,
});
});
}
@@ -1593,7 +1590,8 @@ export default {
} else {
const Galaxy = getGalaxyInstance();
const historyId = Galaxy.currHistoryPanel.model.id;
let elements, targets;
let elements;
let targets;
if (collectionType) {
targets = [];
const elementsByCollectionName = this.creationElementsForFetch();
@@ -1602,7 +1600,7 @@ export default {
destination: { type: "hdca" },
elements: elementsByCollectionName[collectionName],
collection_type: collectionType,
name: collectionName
name: collectionName,
};
if (this.addNameTag) {
target["tags"] = ["name:" + collectionName];
@@ -1615,8 +1613,8 @@ export default {
{
destination: { type: "hdas" },
elements: elements,
name: name
}
name: name,
},
];
}
@@ -1625,7 +1623,7 @@ export default {
.post(`${getAppRoot()}api/tools/fetch`, {
history_id: historyId,
targets: targets,
auto_decompress: true
auto_decompress: true,
})
.then(this.refreshAndWait)
.catch(this.renderFetchError);
@@ -1718,10 +1716,7 @@ export default {
identifiersAtDepth.push(identifier);
} else {
// Create nesting for this element.
collectionTypeAtDepth = collectionTypeAtDepth
.split(":")
.slice(1)
.join(":");
collectionTypeAtDepth = collectionTypeAtDepth.split(":").slice(1).join(":");
let found = false;
for (const element of elementsAtDepth) {
if (element["name"] == identifier) {
@@ -1761,7 +1756,7 @@ export default {
const src = this.elementsType == "datasets" ? "hda" : "ldda";
return { id: source["id"], name: identifier, src: src, tags: res.tags };
},
identifier => {
(identifier) => {
return { name: identifier, src: "new_collection" };
},
"element_identifiers"
@@ -1779,7 +1774,7 @@ export default {
res["name"] = identifier;
return res;
},
identifier => {
(identifier) => {
return { name: identifier };
},
"elements"
@@ -1912,7 +1907,7 @@ export default {
res["tags"] = tags;
}
return res;
}
},
},
created() {
if (this.elementsType !== "collection_contents") {
@@ -1938,28 +1933,28 @@ export default {
}
}
UploadUtils.getUploadDatatypes(
extensions => {
(extensions) => {
this.extensions = extensions;
this.extension = UploadUtils.DEFAULT_EXTENSION;
},
false,
UploadUtils.AUTO_EXTENSION
);
UploadUtils.getUploadGenomes(genomes => {
UploadUtils.getUploadGenomes((genomes) => {
this.genomes = genomes;
this.genome = UploadUtils.DEFAULT_GENOME;
}, UploadUtils.DEFAULT_GENOME);
}
},
watch: {
addColumnRegexType: function(val) {
addColumnRegexType: function (val) {
if (val == "groups") {
this.addColumnRegexGroupCount = 1;
}
if (val == "replacement") {
this.addColumnRegexReplacement = "\0";
}
}
},
},
components: {
HotTable,
@@ -1973,8 +1968,8 @@ export default {
RuleModalHeader,
RuleModalMiddle,
RuleModalFooter,
Select2
}
Select2,
},
};
</script>
@@ -38,20 +38,20 @@ const RuleDisplayPreview = {
props: {
rule: {
required: true,
type: Object
type: Object,
},
colHeaders: {
type: Array,
required: false
}
required: false,
},
},
computed: {
title() {
const ruleType = this.rule.type;
return RuleDefs.RULES[ruleType].display(this.rule, this.colHeaders);
}
},
},
methods: {}
methods: {},
};
const IdentifierDisplayPreview = {
@@ -63,15 +63,15 @@ const IdentifierDisplayPreview = {
props: {
type: {
type: String,
required: true
required: true,
},
columns: {
required: true
required: true,
},
colHeaders: {
type: Array,
required: true
}
required: true,
},
},
computed: {
typeDisplay() {
@@ -82,40 +82,40 @@ const IdentifierDisplayPreview = {
},
columnsLabel() {
return RuleDefs.columnDisplay(this.columns, this.colHeaders);
}
}
},
},
};
export default {
data: function() {
data: function () {
return {};
},
computed: {
mapping: function() {
mapping: function () {
return this.inputRules ? this.inputRules.mapping : [];
},
rules: function() {
rules: function () {
return this.inputRules ? this.inputRules.rules : [];
},
columnData: function() {
columnData: function () {
const colHeadersPerRule = [];
const hotData = RuleDefs.applyRules([], [], [], this.rules, colHeadersPerRule);
return { colHeadersPerRule: colHeadersPerRule, columns: hotData.columns };
},
colHeaders: function() {
colHeaders: function () {
const columns = this.columnData.columns;
return RuleDefs.colHeadersFor([], columns);
}
},
},
props: {
inputRules: {
required: false,
type: Object
}
type: Object,
},
},
components: {
RuleDisplayPreview,
IdentifierDisplayPreview
}
IdentifierDisplayPreview,
},
};
</script>
+11 -15
View File
@@ -5,7 +5,7 @@ import $ from "jquery";
export default {
props: ["options", "value", "placeholder", "containerClass", "enabled"],
template: `<select><slot></slot></select>`,
mounted: function() {
mounted: function () {
const vm = this;
// TODO: refactor property list to objects that allow defaults and types
let enabled = this.enabled;
@@ -17,7 +17,7 @@ export default {
placeholder: this.placeholder,
allowClear: !!this.placeholder,
enable: enabled,
dropdownAutoWidth: true
dropdownAutoWidth: true,
};
if (this.containerClass) {
select2Options.containerCssClass = this.containerClass;
@@ -28,28 +28,24 @@ export default {
.val(this.value)
.trigger("change")
// emit event on change.
.on("change", function(event) {
.on("change", function (event) {
vm.$emit("input", event.val);
});
},
watch: {
value: function(value) {
value: function (value) {
// update value
$(this.$el).val(value);
},
options: function(options) {
options: function (options) {
// update options
$(this.$el)
.empty()
.select2({ data: options });
$(this.$el).empty().select2({ data: options });
},
enabled: function(value) {
enabled: function (value) {
$(this.$el).select2("enable", value);
}
},
},
destroyed: function () {
$(this.$el).off().select2("destroy");
},
destroyed: function() {
$(this.$el)
.off()
.select2("destroy");
}
};
@@ -2,7 +2,7 @@
<b-input-group>
<b-input v-model="filter" placeholder="Type to Search" />
<b-input-group-append>
<b-btn :disabled="!filter" @click="filter = ''"><i class="fa fa-times"/></b-btn>
<b-btn :disabled="!filter" @click="filter = ''"><i class="fa fa-times" /></b-btn>
</b-input-group-append>
</b-input-group>
</template>
@@ -22,8 +22,8 @@ export default {
},
set(val) {
this.$emit("input", val);
}
}
}
},
},
},
};
</script>
@@ -43,20 +43,20 @@ export default {
props: {
items: {
type: Array,
required: true
required: true,
},
filter: {
type: String,
default: null
default: null,
},
multiple: {
type: Boolean,
default: false
default: false,
},
leafIcon: {
type: String,
default: "fa fa-file-o"
}
default: "fa fa-file-o",
},
},
data() {
return {
@@ -64,19 +64,19 @@ export default {
fields: [
{
key: "label",
sortable: true
sortable: true,
},
{
key: "details",
sortable: true
sortable: true,
},
{
key: "time",
sortable: true
}
sortable: true,
},
],
nItems: 0,
perPage: 100
perPage: 100,
};
},
watch: {
@@ -84,19 +84,19 @@ export default {
immediate: true,
handler(items) {
this.filtered(items);
}
}
},
},
},
methods: {
/** Resets pagination when a filter/search word is entered **/
filtered: function(items) {
filtered: function (items) {
this.nItems = items.length;
this.currentPage = 1;
},
/** Collects selected datasets in value array **/
clicked: function(record) {
clicked: function (record) {
this.$emit("clicked", record);
}
}
},
},
};
</script>
@@ -9,7 +9,7 @@ import { shallowMount, createLocalVue } from "@vue/test-utils";
const mockOptions = {
callback: () => {},
modalStatic: true,
history: "f2db41e1fa331b3e"
history: "f2db41e1fa331b3e",
};
describe("DatasetCollectionDialog.vue", () => {
@@ -23,7 +23,7 @@ describe("DatasetCollectionDialog.vue", () => {
localVue = createLocalVue();
wrapper = shallowMount(DatasetCollectionDialog, {
propsData: mockOptions,
localVue: localVue
localVue: localVue,
});
});
@@ -33,8 +33,8 @@ export default {
props: {
history: {
type: String,
required: true
}
required: true,
},
},
data() {
return {
@@ -43,42 +43,42 @@ export default {
items: [],
modalShow: true,
optionsShow: false,
hasValue: false
hasValue: false,
};
},
created: function() {
created: function () {
this.load();
},
methods: {
formatRows() {},
clicked: function(record) {
clicked: function (record) {
this.modalShow = false;
this.callback(record);
},
/** Performs server request to retrieve data records **/
load: function() {
load: function () {
this.filter = null;
this.optionsShow = false;
const Galaxy = getGalaxyInstance();
const url = `${Galaxy.root}api/histories/${this.history}/contents?type=dataset_collection`;
axios
.get(url)
.then(response => {
this.items = response.data.map(item => {
.then((response) => {
this.items = response.data.map((item) => {
return {
id: item.id,
label: item.name,
time: item.created_time,
isLeaf: true
isLeaf: true,
};
});
this.formatRows();
this.optionsShow = true;
})
.catch(errorMessage => {
.catch((errorMessage) => {
this.errorMessage = errorMessageAsString(errorMessage);
});
}
}
},
},
};
</script>
@@ -3,7 +3,7 @@ import { mount, createLocalVue } from "@vue/test-utils";
const mockOptions = {
callback: () => {},
modalStatic: true
modalStatic: true,
};
describe("SelectionDialog.vue", () => {
@@ -19,26 +19,30 @@ describe("SelectionDialog.vue", () => {
wrapper = mount(SelectionDialog, {
slots: {
options: "<tree-options />",
search: "<cool-search />"
search: "<cool-search />",
},
stubs: {
"tree-options": "<div id='tree-options'/>",
"cool-search": "<div id='cool-search'/>",
},
propsData: mockOptions,
localVue
localVue,
});
});
it("loads correctly in loading state, shows options when optionsShow becomes true", async () => {
expect(wrapper.find(".fa-spinner").text()).to.equals("");
expect(wrapper.contains(".fa-spinner")).to.equals(true);
expect(wrapper.contains("tree-options")).to.equals(false);
expect(wrapper.contains("#tree-options")).to.equals(false);
wrapper.setProps({ optionsShow: true });
await localVue.nextTick();
expect(wrapper.contains(".fa-spinner")).to.equals(false);
expect(wrapper.contains("tree-options")).to.equals(true);
expect(wrapper.contains("#tree-options")).to.equals(true);
});
it("loads search correctly", async () => {
await localVue.nextTick();
expect(wrapper.contains("cool-search")).to.equals(true);
expect(wrapper.contains("#cool-search")).to.equals(true);
});
it("hideModal called on click cancel", async () => {
@@ -34,29 +34,29 @@ export default {
props: {
multiple: {
type: Boolean,
default: false
default: false,
},
modalStatic: {
type: Boolean,
default: false
default: false,
},
errorMessage: {
type: String,
default: null
default: null,
},
optionsShow: {
type: Boolean,
default: false
default: false,
},
modalShow: {
type: Boolean,
default: true
default: true,
},
hideModal: {
type: Function,
required: true
}
}
required: true,
},
},
};
</script>
@@ -6,12 +6,12 @@ export default {
components: {
"selection-dialog": SelectionDialog,
"data-dialog-table": DataDialogTable,
"data-dialog-search": DataDialogSearch
"data-dialog-search": DataDialogSearch,
},
props: {
callback: {
type: Function,
required: true
}
}
required: true,
},
},
};
@@ -9,7 +9,7 @@ import { shallowMount, createLocalVue } from "@vue/test-utils";
const mockOptions = {
callback: () => {},
modalStatic: true
modalStatic: true,
};
describe("WorkflowDialog.vue", () => {
@@ -20,11 +20,11 @@ describe("WorkflowDialog.vue", () => {
beforeEach(() => {
axiosMock = new MockAdapter(axios);
setupTestGalaxy();
rewire.__Rewire__("_addAttributes", workflow => workflow);
rewire.__Rewire__("_addAttributes", (workflow) => workflow);
localVue = createLocalVue();
wrapper = shallowMount(WorkflowDialog, {
propsData: mockOptions,
localVue: localVue
localVue: localVue,
});
});
@@ -36,41 +36,41 @@ export default {
items: [],
modalShow: true,
optionsShow: false,
hasValue: false
hasValue: false,
};
},
created: function() {
created: function () {
this.services = new Services();
this.load();
},
methods: {
formatRows() {},
clicked: function(record) {
clicked: function (record) {
this.modalShow = false;
this.callback(record);
},
/** Performs server request to retrieve data records **/
load: function() {
load: function () {
this.filter = null;
this.optionsShow = false;
this.services
.getWorkflows()
.then(items => {
this.items = items.map(item => {
.then((items) => {
this.items = items.map((item) => {
return {
id: item.id,
label: item.name,
time: item.create_time,
isLeaf: true
isLeaf: true,
};
});
this.formatRows();
this.optionsShow = true;
})
.catch(errorMessage => {
.catch((errorMessage) => {
this.errorMessage = errorMessage.toString();
});
}
}
},
},
};
</script>
+27 -27
View File
@@ -22,7 +22,7 @@
<p>Anyone can view and import this {{ model_class_lc }} by visiting the following URL:</p>
<blockquote>
<a id="item-url" :href="item_url" target="_top">{{ item_url }}</a>
<span id="item-url-text" style="display: none">
<span id="item-url-text" style="display: none;">
{{ item_url_parts[0] }}<span id="item-identifier">{{ item_url_parts[1] }}</span>
</span>
<a href="javascript:void(0)" id="edit-identifier"
@@ -150,16 +150,16 @@ export default {
props: {
id: {
type: String,
required: true
required: true,
},
plural_name: {
type: String,
required: true
required: true,
},
model_class: {
type: String,
required: true
}
required: true,
},
},
computed: {
model_class_lc() {
@@ -196,7 +196,7 @@ export default {
},
show_danger() {
return this.err_msg !== null;
}
},
},
data() {
const Galaxy = getGalaxyInstance();
@@ -211,68 +211,68 @@ export default {
username_and_slug: "username_and_slug",
importable: false,
published: false,
users_shared_with: []
users_shared_with: [],
},
share_fields: ["email", { key: "id", label: "" }],
make_members_public: false
make_members_public: false,
};
},
created: function() {
created: function () {
this.getModel();
},
updated: function() {
updated: function () {
this.createSlugHandler();
},
methods: {
getModel: function() {
getModel: function () {
this.ready = false;
axios
.get(`${getAppRoot()}api/${this.plural_name_lc}/${this.id}/sharing`)
.then(response => {
.then((response) => {
this.item = response.data;
this.ready = true;
})
.catch(error => (this.err_msg = error.response.data.err_msg));
.catch((error) => (this.err_msg = error.response.data.err_msg));
},
setUsername: function() {
setUsername: function () {
const Galaxy = getGalaxyInstance();
axios
.put(`${getAppRoot()}api/users/${Galaxy.user.id}/information/inputs`, {
username: this.new_username || ""
username: this.new_username || "",
})
.then(response => {
.then((response) => {
this.err_msg = null;
this.has_username = true;
this.getModel();
})
.catch(error => (this.err_msg = error.response.data.err_msg));
.catch((error) => (this.err_msg = error.response.data.err_msg));
},
setSharing: function(action, user_id) {
setSharing: function (action, user_id) {
const data = {
action: action,
user_id: user_id
user_id: user_id,
};
if (this.has_possible_members) {
data.make_members_public = this.make_members_public;
}
axios
.post(`${getAppRoot()}api/${this.plural_name_lc}/${this.id}/sharing`, data)
.then(response => {
.then((response) => {
Object.assign(this.item, response.data);
if (response.data.skipped) {
this.err_msg = "Some of the items within this object were not published due to an error.";
}
})
.catch(error => (this.err_msg = error.response.data.err_msg));
.catch((error) => (this.err_msg = error.response.data.err_msg));
},
createSlugHandler: function() {
const on_start = function(text_elt) {
createSlugHandler: function () {
const on_start = function (text_elt) {
// Replace URL with URL text.
$("#item-url").hide();
$("#item-url-text").show();
// Allow only lowercase alphanumeric and '-' characters in slug.
text_elt.keyup(function() {
text_elt.keyup(function () {
text_elt.val(
$(this)
.val()
@@ -282,7 +282,7 @@ export default {
);
});
};
const on_finish = function(text_elt) {
const on_finish = function (text_elt) {
// Replace URL text with URL.
$("#item-url-text").hide();
$("#item-url").show();
@@ -304,7 +304,7 @@ export default {
on_start,
on_finish
);
}
}
},
},
};
</script>
@@ -5,7 +5,7 @@ import Vue from "vue";
describe("Tags/StatelessTags.vue", () => {
const localVue = createLocalVue();
localVue.filter("localize", value => _l(value));
localVue.filter("localize", (value) => _l(value));
let testTags = ["abc", "def", "ghi"];
let wrapper, emitted;
@@ -13,7 +13,7 @@ describe("Tags/StatelessTags.vue", () => {
beforeEach(async () => {
wrapper = mount(StatelessTags, { localVue });
wrapper.setProps({
value: testTags
value: testTags,
});
emitted = wrapper.emitted();
await Vue.nextTick();
@@ -11,7 +11,7 @@ upstream component or environment that is accessed through props and events -->
class="toggle-link"
@click.prevent="toggleTagDisplay"
>
{{ linkText | localize }}
{{ linkText }}
</a>
<vue-tags-input
v-if="tagsVisible"
@@ -20,7 +20,7 @@ upstream component or environment that is accessed through props and events -->
:tags="tagModels"
:autocomplete-items="autocompleteTags"
:disabled="disabled"
:placeholder="'Add Tags' | localize"
placeholder="Add Tags"
:add-on-key="triggerKeys"
@before-adding-tag="beforeAddingTag"
@before-deleting-tag="beforeDeletingTag"
@@ -39,14 +39,14 @@ import { createTag } from "./model";
export default {
components: {
VueTagsInput
VueTagsInput,
},
props: {
value: { type: Array, required: false, default: () => [] },
autocompleteItems: { type: Array, required: false, default: () => [] },
maxVisibleTags: { type: Number, required: false, default: 5 },
useToggleLink: { type: Boolean, required: false, default: true },
disabled: { type: Boolean, required: false, default: false }
disabled: { type: Boolean, required: false, default: false },
},
data() {
// initialize toggle value
@@ -55,13 +55,13 @@ export default {
return {
tagText: "",
tagToggle: !isClosed,
triggerKeys: [13, " "]
triggerKeys: [13, " "],
};
},
computed: {
tagContainerClasses() {
return {
disabled: this.disabled
disabled: this.disabled,
};
},
tagModels() {
@@ -78,19 +78,19 @@ export default {
},
tagsVisible() {
return this.useToggleLink ? this.tagToggle : true;
}
},
},
watch: {
tagText(newValue) {
this.$emit("tag-input-changed", newValue);
}
},
},
methods: {
tagsChanged(newTags) {
this.$emit("input", this.pluckLabels(newTags));
},
pluckLabels(newTags) {
return newTags.map(t => createTag(t).toString());
return newTags.map((t) => createTag(t).toString());
},
toggleTagDisplay() {
this.tagToggle = !this.tagToggle;
@@ -117,8 +117,8 @@ export default {
},
hasHandler(eventName) {
return Object.keys(this.$listeners).includes(eventName);
}
}
},
},
};
</script>

Some files were not shown because too many files have changed in this diff Show More