mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge branch 'dev' into container_monitor
This commit is contained in:
Symlink
+1
@@ -0,0 +1 @@
|
||||
api
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --skip_flakey_fails -api "$@"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
framework
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --framework "$@"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
integration
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOCKER_RUN_EXTRA_ARGS="--privileged" ./run_tests.sh --dockerize --python3 --db postgres --clean_pyc --integration "$@"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
main-tools
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
./run_tests.sh --dockerize --python3 --db postgres --clean_pyc -main "$@"
|
||||
@@ -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"
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
selenium
|
||||
@@ -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 "$@"
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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,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'
|
||||
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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 |
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+20
@@ -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);
|
||||
});
|
||||
};
|
||||
+12
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user