diff --git a/client/models/model-exploration-settings.js b/client/models/model-exploration-settings.js new file mode 100644 index 0000000000..7359e194d8 --- /dev/null +++ b/client/models/model-exploration-settings.js @@ -0,0 +1,46 @@ +/* +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2020 StochSS developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +//models +var State = require('ampersand-state'); +//collections +var ModelExplorationVariables = require('./model-exploration-variables'); + +module.exports = State.extend({ + props: { + distributionType: 'string' + }, + collections: { + variables: ModelExplorationVariables + }, + initialize: function(attrs, options) { + State.prototype.initialize.apply(this, arguments); + }, + updateVariables: function (parameters) { + this.variables.forEach(function (variable) { + let parameter = parameters.filter(function (parameter) { + return parameter.compID === variable.paramID + })[0] + if(parameter === undefined) { + this.removeVariable(variable) + }else{ + variable.updateVariable(variable) + } + }) + } +}); diff --git a/client/models/model-exploration-variable.js b/client/models/model-exploration-variable.js new file mode 100644 index 0000000000..756d39ef90 --- /dev/null +++ b/client/models/model-exploration-variable.js @@ -0,0 +1,45 @@ +/* +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2020 StochSS developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +//models +var State = require('ampersand-state'); + +module.exports = State.extend({ + props: { + paramID: 'number', + min: 'any', + max: 'any', + steps: 'any', + level: 'any', + outliers: 'string', + seedSize: 'any', + hasChangedRanged: 'boolean' + }, + initialize: function(attrs, options) { + State.prototype.initialize.apply(this, arguments); + }, + updateVariable: function (parameter) { + let value = parameter.expression + if(this.min <= 0 || !this.hasChangedRanged) { + this.min = value * 0.5 + } + if(this.max <= 0 || !this.hasChangedRanged) { + this.max = value * 1.5 + } + } +}); \ No newline at end of file diff --git a/client/models/model-exploration-variables.js b/client/models/model-exploration-variables.js new file mode 100644 index 0000000000..9388a88654 --- /dev/null +++ b/client/models/model-exploration-variables.js @@ -0,0 +1,41 @@ +/* +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2020 StochSS developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +//models +var Variable = require('./model-exploration-variable'); +//collections +var Collection = require('ampersand-collection'); + +module.exports = Collection.extend({ + model: Variable, + addVariable: function (paramID) { + var variable = this.add({ + paramID: paramID, + min: 0, + max: 0, + steps: 11, + level: 1, + outliers: "", + seedSize: 1, + hasChangedRange: false + }); + }, + removeVariable: function (variable) { + this.remove(variable); + } +}); \ No newline at end of file diff --git a/client/models/settings.js b/client/models/settings.js index 7b80a1271c..3e934d139e 100644 --- a/client/models/settings.js +++ b/client/models/settings.js @@ -20,12 +20,14 @@ along with this program. If not, see . var State = require('ampersand-state'); var SimulationSettings = require('./simulation-settings'); var ParameterSweepSettings = require('./parameter-sweep-settings'); +var ModelExplorationSettings = require('./model-exploration-settings'); var ResultsSettings = require('./results-settings'); module.exports = State.extend({ children: { simulationSettings: SimulationSettings, parameterSweepSettings: ParameterSweepSettings, + modelExplorationSettings: ModelExplorationSettings, resultsSettings: ResultsSettings }, initialize: function(attrs, options) { diff --git a/client/pages/workflow-selection.js b/client/pages/workflow-selection.js index 0254d31ee8..2903b2d280 100644 --- a/client/pages/workflow-selection.js +++ b/client/pages/workflow-selection.js @@ -40,7 +40,8 @@ let workflowSelection = PageView.extend({ "click [data-hook=sciope-model-exploration]" : "notebookWorkflow", "click [data-hook=model-inference]" : "notebookWorkflow", "click [data-hook=stochss-es]" : "handleEnsembleSimulationClick", - "click [data-hook=stochss-ps]" : "handleParameterSweepClick" + "click [data-hook=stochss-ps]" : "handleParameterSweepClick", + "click [data-hook=stochss-me]" : "handleModelExplorationClick" }, initialize: function (attrs, options) { PageView.prototype.initialize.apply(this, arguments); @@ -147,6 +148,9 @@ let workflowSelection = PageView.extend({ handleParameterSweepClick: function (e) { this.launchStochssWorkflow("parameterSweep") }, + handleModelExplorationClick: function (e) { + this.launchStochssWorkflow("modelExploration") + }, launchStochssWorkflow: function (type) { let queryString = "?type=" + type + "&path=" + this.modelDir + "&parentPath=" + this.parentPath let endpoint = path.join(app.getBasePath(), "stochss/workflow/edit")+queryString diff --git a/client/templates/includes/modelExplorationSettings.pug b/client/templates/includes/modelExplorationSettings.pug new file mode 100644 index 0000000000..fef17738dc --- /dev/null +++ b/client/templates/includes/modelExplorationSettings.pug @@ -0,0 +1,36 @@ +div#model-exploration-settings.card.card-body + + div + + h3.inline Model Explorataion Settings + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-mexplore-settings", data-hook="collapse") - + + div.collapse(class="show", id="collapse-mexplore-settings") + + div.row + + div.col-md-4 + + span.inline(for="distribution-type") Distribution Type: + + div.inline(id="distribution-type" data-hook="distribution-type") + + div.col-md-4(style="display: none") + + span.inline(for="exploration-type") Exploration Type: + + div.inline(id="exploration-type" data-hook="exploration-type") + + div.col-md-4(style="display: none") + + span.inline(for="feature-extraction") Feature Extraction: + + div.inline(id="feature-extraction" data-hook="feature-extraction") + + div + + h5 Configure Variable(s) + + div(data-hook="me-variables-collection") + + button.btn.btn-outline-secondary.box-shadow(data-hook="add-me-variable") Add Variable diff --git a/client/templates/includes/modelExplorationVariable.pug b/client/templates/includes/modelExplorationVariable.pug new file mode 100644 index 0000000000..99a071181c --- /dev/null +++ b/client/templates/includes/modelExplorationVariable.pug @@ -0,0 +1,25 @@ +tr + + td: div(data-hook="variable-target") + + td: div(data-hook="target-value")=this.parameter.expression + + td: div(data-hook="variable-min") + + td: div(data-hook="variable-max") + + td + + if(this.distributionType == "Uniform") + div(data-hook="variable-steps") + + if(this.distributionType == "Factorial") + div(data-hook="variable-level") + + if(this.distributionType == "Latin Hypercube") + div(data-hook="variable-seed-size") + + if(this.distributionType == "Factorial") + td: div(data-hook="variable-outliers") + + td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file diff --git a/client/templates/includes/modelExplorationVariables.pug b/client/templates/includes/modelExplorationVariables.pug new file mode 100644 index 0000000000..31f7c0d753 --- /dev/null +++ b/client/templates/includes/modelExplorationVariables.pug @@ -0,0 +1,40 @@ +table.table + thead + tr + th(scope="col") + div + div.inline Sweep Target + + th(scope="col") + div + div.inline Current Value + + th(scope="col") + div + div.inline Minimum Value + + th(scope="col") + div + div.inline Maximum Value + + th(scope="col") + if(this.distributionType == "Uniform") + div + div.inline Steps + + if(this.distributionType == "Factorial") + div + div.inline Level + + if(this.distributionType == "Latin Hypercube") + div + div.inline Seed Size + + if(this.distributionType == "Factorial") + th(scope="col") + div + div.inline Outliers + + th(scope="col"): div Remove + + tbody(data-hook="me-variables") \ No newline at end of file diff --git a/client/templates/includes/workflowEditor.pug b/client/templates/includes/workflowEditor.pug index 631555a4d6..4e0e4f4c1a 100644 --- a/client/templates/includes/workflowEditor.pug +++ b/client/templates/includes/workflowEditor.pug @@ -12,6 +12,8 @@ div#workflow-editor.card.card-body div.collapse(class="show" data-hook="param-sweep-settings-container") + div.collapse(class="show" data-hook="model-exploration-settings-container") + div(data-hook="sim-settings-container") div(data-hook="workflow-state-buttons-container") \ No newline at end of file diff --git a/client/templates/pages/workflowSelection.pug b/client/templates/pages/workflowSelection.pug index 4b8c34d333..e5e44efc81 100644 --- a/client/templates/pages/workflowSelection.pug +++ b/client/templates/pages/workflowSelection.pug @@ -31,6 +31,9 @@ section.page div.tooltip-icon.horizontal-space(data-html="true" data-toggle="tooltip" title=this.tooltips.parameterSweep) + td + button.btn.btn-outline-primary.box-shadow.inline(id="stochss-me" data-hook="stochss-me") Model Exploration + table.table thead tr diff --git a/client/views/model-exploration-settings.js b/client/views/model-exploration-settings.js new file mode 100644 index 0000000000..9bcc55f179 --- /dev/null +++ b/client/views/model-exploration-settings.js @@ -0,0 +1,112 @@ +let $ = require('jquery'); +//views +let View = require('ampersand-view'); +let SelectView = require('ampersand-select-view'); +let VariablesCollectionView = require('./model-exploration-variables'); +//templates +let template = require('../templates/includes/modelExplorationSettings.pug'); + +module.exports = View.extend({ + template: template, + events: { + 'change [data-hook=distribution-type]' : 'selectDistributionType', + 'click [data-hook=add-me-variable]' : 'handleAddVariableClick', + 'click [data-hook=collapse]' : 'changeCollapseButtonText' + }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments) + this.stochssModel = attrs.stochssModel + this.model.variables.on("add remove", function () { + let disable = this.model.variables.length >= 2 || this.model.variables.length >= this.stochssModel.parameters.length + $(this.queryByHook("add-me-variable")).prop("disabled", disable) + }, this) + }, + render: function (attrs, options) { + View.prototype.render.apply(this, arguments) + this.renderDistributionTypeSelect(); + this.renderExplorationTypeSelect(); + this.renderFeatureExtractionSelect(); + this.model.updateVariables(this.stochssModel.parameters) + this.renderVariablesCollection() + }, + renderDistributionTypeSelect: function () { + let options = ["Uniform", "Factorial", "Latin Hypercube"] + var distributionTypeView = new SelectView({ + label: '', + name: 'distribution-type', + required: true, + idAttribute: 'cid', + options: options, + value: this.model.distributionType + }); + this.registerRenderSubview(distributionTypeView, "distribution-type") + }, + renderExplorationTypeSelect: function () { + let options = ["Basic Exploration"] + var explorationTypeView = new SelectView({ + label: '', + name: 'distribution-type', + required: true, + idAttribute: 'cid', + options: options, + value: "Basic Exploration" + }); + this.registerRenderSubview(explorationTypeView, "exploration-type") + }, + renderFeatureExtractionSelect: function () { + let options = ["Minimum of population", "Maximum of population", "Average of population", + "Variance of population", "Population at last time point"] + var featureExtractionView = new SelectView({ + label: '', + name: 'distribution-type', + required: true, + idAttribute: 'cid', + options: options, + value: "Population at last time point" + }); + this.registerRenderSubview(featureExtractionView, "feature-extraction") + }, + renderVariablesCollection: function () { + if(this.variablesView) { + this.variablesView.remove() + } + this.variablesView = new VariablesCollectionView({ + parent: this, + collection: this.model.variables, + distributionType: this.model.distributionType, + stochssModel: this.stochssModel + }); + this.registerRenderSubview(this.variablesView, "me-variables-collection") + }, + registerRenderSubview: function (view, hook) { + this.registerSubview(view); + this.renderSubview(view, this.queryByHook(hook)); + }, + selectDistributionType: function (e) { + let type = e.target.selectedOptions.item(0).text; + this.model.distributionType = type; + this.renderVariablesCollection() + }, + getParameterID: function () { + let variableTargets = this.model.variables.map(function (variable) { return variable.paramID }); + let target = this.stochssModel.parameters.filter(function (param) { + return !variableTargets.includes(param.compID) + })[0].compID + return target + }, + handleAddVariableClick: function () { + let target = this.getParameterID() + this.model.variables.addVariable(target) + }, + changeCollapseButtonText: function (e) { + if(e.target.dataset && e.target.dataset.toggle === "collapse") { + let source = e.target.dataset.hook + let collapseContainer = $(this.queryByHook(source).dataset.target) + if(!collapseContainer.length || !collapseContainer.attr("class").includes("collapsing")) { + let collapseBtn = $(this.queryByHook(source)) + let text = collapseBtn.text(); + text === '+' ? collapseBtn.text('-') : collapseBtn.text('+'); + } + } + } +}); \ No newline at end of file diff --git a/client/views/model-exploration-variable.js b/client/views/model-exploration-variable.js new file mode 100644 index 0000000000..51423d06e0 --- /dev/null +++ b/client/views/model-exploration-variable.js @@ -0,0 +1,207 @@ +let $ = require('jquery'); +let tests = require('./tests'); +//views +let View = require('ampersand-view'); +let InputView = require('./input'); +let SelectView = require('ampersand-select-view'); +//templates +let template = require('../templates/includes/modelExplorationVariable.pug'); + +module.exports = View.extend({ + template: template, + events: { + 'change [data-hook=variable-target]' : 'setSelectedTarget', + 'change [data-hook=variable-min]' : 'setHasChangedRange', + 'change [data-hook=variable-max]' : 'setHasChangedRange', + 'click [data-hook=remove]' : 'removeVariable' + }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + let self = this + this.distributionType = this.parent.distributionType + this.parameters = this.parent.stochssModel.parameters + this.parameter = this.parameters.filter(function (param) { + return param.compID === self.model.paramID + })[0] + this.model.updateVariable(this.parameter) + this.model.collection.on('add update-target remove', this.registerTargetSelectView, this) + }, + render: function (attrs, options) { + View.prototype.render.apply(this, arguments); + this.registerTargetSelectView() + this.renderMinValInputView() + this.renderMaxValInputView() + if(this.parent.parent.model.distributionType === "Uniform"){ + this.renderStepsInputView() + }else if(this.parent.parent.model.distributionType === "Factorial") { + this.renderLevelInputView() + this.renderOutliersInputView() + }else if(this.parent.parent.model.distributionType === "Latin Hypercube") { + this.renderSeedSizeInputView() + } + }, + update: function (e) {}, + updateValid: function (e) {}, + registerTargetSelectView: function (e) { + if(this.model.collection) { + if(this.targetSelectView) { + this.targetSelectView.remove() + } + let options = this.getAvailableParameters(); + this.targetSelectView = new SelectView({ + label: '', + name: 'variable-target', + required: true, + idAttribute: 'cid', + options: options, + value: this.parameter.name + }); + this.registerRenderSubview(this.targetSelectView, "variable-target") + } + }, + renderMinValInputView: function () { + if(this.minValInputView) { + this.minValInputView.remove() + } + this.minValInputView = new InputView({ + parent: this, + required: true, + name: 'min-value', + label: '', + tests: tests.valueTests, + modelKey: 'min', + valueType: 'number', + value: this.model.min, + }); + this.registerRenderSubview(this.minValInputView, "variable-min") + }, + renderMaxValInputView: function () { + if(this.maxValInputView) { + this.maxValInputView.remove() + } + this.maxValInputView = new InputView({ + parent: this, + required: true, + name: 'max-value', + label: '', + tests: tests.valueTests, + modelKey: 'max', + valueType: 'number', + value: this.model.max, + }); + this.registerRenderSubview(this.maxValInputView, "variable-max") + }, + renderStepsInputView: function () { + if(this.stepsInputView) { + this.stepsInputView.remove() + } + this.stepsInputView = new InputView({ + parent: this, + required: true, + name: 'steps', + label: '', + tests: tests.valueTests, + modelKey: 'steps', + valueType: 'number', + value: this.model.steps, + }); + this.registerRenderSubview(this.stepsInputView, "variable-steps") + }, + renderLevelInputView: function () { + if(this.levelInputView) { + this.levelInputView.remove() + } + this.levelInputView = new InputView({ + parent: this, + required: true, + name: 'level', + label: '', + tests: tests.valueTests, + modelKey: 'level', + valueType: 'number', + value: this.model.level, + }); + this.registerRenderSubview(this.levelInputView, "variable-level") + }, + renderOutliersInputView: function () { + if(this.outliersInputView) { + this.outliersInputView.remove() + } + this.outliersInputView = new InputView({ + parent: this, + required: false, + name: 'outliers', + label: '', + tests: [], + modelKey: 'outliers', + valueType: 'string', + value: this.model.outliers, + placeholder: "--Enter Outliers--" + }); + this.registerRenderSubview(this.outliersInputView, "variable-outliers") + }, + renderSeedSizeInputView: function () { + if(this.seedSizeInputView) { + this.seedSizeInputView.remove() + } + this.seedSizeInputView = new InputView({ + parent: this, + required: true, + name: 'seed-size', + label: '', + tests: tests.valueTests, + modelKey: 'seedSize', + valueType: 'number', + value: this.model.seedSize, + }); + this.registerRenderSubview(this.seedSizeInputView, "variable-seed-size") + }, + registerRenderSubview: function (view, hook) { + this.registerSubview(view); + this.renderSubview(view, this.queryByHook(hook)); + }, + getAvailableParameters: function () { + let variableTargets = this.model.collection.map(function (variable) { return variable.paramID}) + let availableParameters = this.parameters.filter(function (param) { + return !variableTargets.includes(param.compID) + }).map(function (param) { return param.name }); + if(!availableParameters.includes(this.parameter.name)) { + availableParameters.push(this.parameter.name) + } + return availableParameters + }, + setSelectedTarget: function (e) { + let targetName = e.target.selectedOptions.item(0).text; + this.parameter = this.parameters.filter(function (param) { + return param.name === targetName + })[0] + let targetID = this.parameter.compID + this.model.paramID = targetID + this.updateVariableProps() + this.model.collection.trigger('update-target') + }, + setHasChangedRange: function () { + this.model.hasChangedRange = true + }, + updateVariableProps: function () { + $(this.queryByHook("target-value")).text(this.parameter.expression) + this.model.hasChangedRange = false + this.model.updateVariable(this.parameter) + this.renderMinValInputView() + this.renderMaxValInputView() + if(this.parent.parent.model.distributionType === "Uniform"){ + this.renderStepsInputView() + }else if(this.parent.parent.model.distributionType === "Factorial") { + this.renderLevelInputView() + this.renderOutliersInputView() + }else if(this.parent.parent.model.distributionType === "Latin Hypercube") { + this.renderSeedSizeInputView() + } + }, + removeVariable: function () { + // console.log("Removing variable targeting " + this.parameter.name) + this.remove() + this.model.collection.removeVariable(this.model) + this.remove() + } +}); \ No newline at end of file diff --git a/client/views/model-exploration-variables.js b/client/views/model-exploration-variables.js new file mode 100644 index 0000000000..dedf7e2be2 --- /dev/null +++ b/client/views/model-exploration-variables.js @@ -0,0 +1,32 @@ +let $ = require('jquery'); +//views +let View = require('ampersand-view'); +let VariableView = require('./model-exploration-variable'); +//templates +let template = require('../templates/includes/modelExplorationVariables.pug'); + +module.exports = View.extend({ + template: template, + events: {}, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.distributionType = attrs.distributionType + this.stochssModel = attrs.stochssModel + }, + render: function (attrs, options) { + View.prototype.render.apply(this, arguments); + this.renderVariablesCollection() + }, + update: function (e) {}, + updateValid: function (e) {}, + renderVariablesCollection: function () { + if(this.variablesCollection) { + this.variablesCollection.remove() + } + this.variablesCollection = this.renderCollection( + this.collection, + VariableView, + this.queryByHook("me-variables") + ) + } +}); \ No newline at end of file diff --git a/client/views/workflow-editor.js b/client/views/workflow-editor.js index 02c3674d81..1ed2a861f6 100644 --- a/client/views/workflow-editor.js +++ b/client/views/workflow-editor.js @@ -27,6 +27,7 @@ var SimSettingsView = require('./simulation-settings'); var SimulationSettingsViewer = require('./simulation-settings-viewer'); var ParamSweepSettingsView = require('./parameter-sweep-settings'); var ParameterSweepSettingsViewer = require('./parameter-sweep-settings-viewer'); +var ModelExploreSettingsView = require('./model-exploration-settings'); var WorkflowStateButtonsView = require('./workflow-state-buttons'); //templates var template = require('../templates/includes/workflowEditor.pug'); @@ -53,6 +54,8 @@ module.exports = View.extend({ if(this.type === "parameterSweep"){ this.validatePsweep() this.renderParameterSweepSettingsView() + }else if(this.type === "modelExploration") { + this.renderModelExplorationSettings() } this.renderWorkflowStateButtons() }else{ @@ -97,6 +100,17 @@ module.exports = View.extend({ }); this.registerRenderSubview(parameterSweepSettingsView, 'param-sweep-settings-container'); }, + renderModelExplorationSettings: function () { + if(this.modelExplorationSettings) { + this.modelExplorationSettings.remove() + } + this.modelExplorationSettings = new ModelExploreSettingsView({ + parent: this, + model: this.settings.modelExplorationSettings, + stochssModel: this.model + }); + this.registerRenderSubview(this.modelExplorationSettings, 'model-exploration-settings-container') + }, renderWorkflowStateButtons: function () { if(this.workflowStateButtons) { this.workflowStateButtons.remove() diff --git a/stochss/handlers/__init__.py b/stochss/handlers/__init__.py index 6719ee5c47..dadc8c10ed 100644 --- a/stochss/handlers/__init__.py +++ b/stochss/handlers/__init__.py @@ -27,6 +27,12 @@ from .log import init_log def get_page_handlers(route_start): + ''' + Get the list of all page and api handlers + + Atributes + --------- + ''' handlers = [ # ## Page Handlers diff --git a/stochss/handlers/file_browser.py b/stochss/handlers/file_browser.py index 39a00b7d1d..edec479544 100644 --- a/stochss/handlers/file_browser.py +++ b/stochss/handlers/file_browser.py @@ -16,14 +16,6 @@ along with this program. If not, see . ''' -''' -APIHandler documentation: -https://github.com/jupyter/notebook/blob/master/notebook/base/handlers.py#L583 - -Note APIHandler.finish() sets Content-Type handler to 'application/json' - -Use finish() for json, write() for text -''' import os import json import uuid @@ -44,7 +36,7 @@ from .util.convert_to_sbml import convert_to_sbml from .util.convert_sbml_to_model import convert_sbml_to_model from .util.generate_zip_file import download_zip -from .util.upload_file import upload, upload_from_link +from .util.upload_file import upload from .util.workflow_status import get_status log = logging.getLogger('stochss') @@ -895,14 +887,18 @@ async def get(self): if cmd is None: outfile = str(uuid.uuid4()).replace("-", "_")+".tmp" log.debug("Response file name: %s", outfile) - exec_cmd = ['/stochss/stochss/handlers/util/upload_file.py', '{0}'.format(path), '{}'.format(outfile)] # Script commands for read run_cmd + # Script commands for read run_cmd + exec_cmd = ['/stochss/stochss/handlers/util/upload_file.py', + '{0}'.format(path), '{}'.format(outfile)] log.debug("Exec command: %s", exec_cmd) pipe = subprocess.Popen(exec_cmd) resp = {"responsePath": outfile} log.debug("Response: %s", resp) self.write(resp) else: - exec_cmd = ['/stochss/stochss/handlers/util/upload_file.py', 'None', '{}'.format(path)] # Script commands for read run_cmd + # Script commands for read run_cmd + exec_cmd = ['/stochss/stochss/handlers/util/upload_file.py', + 'None', '{}'.format(path)] log.debug("Exec command: %s", exec_cmd) pipe = subprocess.Popen(exec_cmd, stdout=subprocess.PIPE, text=True) results, error = pipe.communicate() diff --git a/stochss/handlers/log.py b/stochss/handlers/log.py index 795f7f09eb..4e1e7828b8 100644 --- a/stochss/handlers/log.py +++ b/stochss/handlers/log.py @@ -22,10 +22,17 @@ log = logging.getLogger('stochss') def init_log(): - ch = logging.StreamHandler() - formatter = LogFormatter(fmt='%(color)s[%(levelname)1.1s %(asctime)s StochSS %(filename)s:%(lineno)d]%(end_color)s %(message)s', datefmt='%H:%M:%S') - ch.setFormatter(formatter) + ''' + Set up StochSS logger + + Attributes + ---------- + ''' + handler = logging.StreamHandler() + formatter = LogFormatter(fmt='%(color)s[%(levelname)1.1s %(asctime)s \ + StochSS %(filename)s:%(lineno)d]%(end_color)s %(message)s', + datefmt='%H:%M:%S') + handler.setFormatter(formatter) log.setLevel(logging.WARNING) - log.addHandler(ch) + log.addHandler(handler) log.propagate = False - diff --git a/stochss/handlers/models.py b/stochss/handlers/models.py index c2e23a00a9..4f6572a66e 100644 --- a/stochss/handlers/models.py +++ b/stochss/handlers/models.py @@ -52,62 +52,100 @@ async def get(self): purpose = self.get_query_argument(name="for") log.debug("Purpose of the handler: %s", purpose) file_path = self.get_query_argument(name="path") - log.debug("Path to the file: {0}".format(file_path)) + log.debug("Path to the file: %s", file_path) if file_path.startswith('/'): file_path = file_path.replace('/', '', 1) full_path = os.path.join('/home/jovyan', file_path) - log.debug("Full path to the file: {0}".format(full_path)) + log.debug("Full path to the file: %s", full_path) self.set_header('Content-Type', 'application/json') if os.path.exists(full_path): - with open(full_path, 'r') as f: - data = json.load(f) - log.debug("Contents of the json file: {0}".format(data)) - if full_path.endswith(".mdl"): - self.update_model_data(data) - if "volume" not in data.keys(): - data['volume'] = data['modelSettings']['volume'] - del data['modelSettings']['volume'] - self.write(data) + self.read_json_file(full_path) elif purpose == "edit": - new_path = '/stochss/stochss_templates/nonSpatialModelTemplate.json' - log.debug("Path to the model template: {0}".format(new_path)) - try: - with open(new_path, 'r') as json_file: - template = json.load(json_file) - log.debug("Contents of the model template: {0}".format(template)) - directories = os.path.dirname(full_path) - log.debug("Path of parent directories: {0}".format(directories)) - try: - os.makedirs(directories) - except FileExistsError: - log.debug("The directories in the path to the model already exists.") - with open(full_path, 'w') as file: - json.dump(template, file) - self.write(template) - except FileNotFoundError as err: - self.set_status(404) - error = {"Reason":"Model Template Not Found", "Message":"Could not find the model template file: "+str(err)} - trace = traceback.format_exc() - log.error("Exception information: {0}\n{1}".format(error, trace)) - error['Traceback'] = trace - self.write(error) - except JSONDecodeError as err: - self.set_status(406) - error = {"Reason":"Template Data Not JSON Format", "Message":"Template data is not JSON decodeable: "+str(err)} - trace = traceback.format_exc() - log.error("Exception information: {0}\n{1}".format(error, trace)) - error['Traceback'] = trace - self.write(error) + self.write_new_model_file(full_path) else: self.set_status(404) - error = {"Reason":"Model Not Found", "Message":"Could not find the model file: "+file_path} - log.error("Exception information: {0}".format(error)) + error = {"Reason":"Model Not Found", + "Message":"Could not find the model file: "+file_path} + log.error("Exception information: %s", error) self.write(error) self.finish() + def read_json_file(self, full_path): + ''' + Get the contents of the target json file + + Attributes + ---------- + full_path : str + Path to the target json file + ''' + with open(full_path, 'r') as file: + data = json.load(file) + log.debug("Contents of the json file: %s", data) + if full_path.endswith(".mdl"): + self.update_model_data(data) + if "volume" in data['modelSettings'].keys(): + data['volume'] = data['modelSettings']['volume'] + elif "volume" not in data.keys(): + data['volume'] = 1 + self.write(data) + + + def write_new_model_file(self, full_path): + ''' + Write a new model file using the model template + + Attributes + ---------- + full_path : str + Path to the new model file + ''' + new_path = '/stochss/stochss_templates/nonSpatialModelTemplate.json' + log.debug("Path to the model template: %s", new_path) + try: + with open(new_path, 'r') as json_file: + template = json.load(json_file) + log.debug("Contents of the model template: %s", template) + directories = os.path.dirname(full_path) + log.debug("Path of parent directories: %s", directories) + try: + os.makedirs(directories) + except FileExistsError: + log.debug("The directories in the path to the model already exists.") + with open(full_path, 'w') as file: + json.dump(template, file) + self.write(template) + except FileNotFoundError as err: + self.set_status(404) + error = {"Reason":"Model Template Not Found", + "Message":"Could not find the model template file: "+str(err)} + trace = traceback.format_exc() + log.error("Exception information: %s", "{0}\n{1}".format(error, trace)) + error['Traceback'] = trace + self.write(error) + except JSONDecodeError as err: + self.set_status(406) + error = {"Reason":"Template Data Not JSON Format", + "Message":"Template data is not JSON decodeable: "+str(err)} + trace = traceback.format_exc() + log.error("Exception information: %s", "{0}\n{1}".format(error, trace)) + error['Traceback'] = trace + self.write(error) + + @classmethod def update_parameter(cls, param, param_ids): + ''' + Update the expression of a StochSS Parameter + + Attributes + ---------- + param : dict + StochSS Parameter + param_ids : list + list of compIDs for the StochSS parameters collection + ''' try: param_ids.append(param['compID']) if isinstance(param['expression'], str): @@ -121,10 +159,19 @@ def update_parameter(cls, param, param_ids): @classmethod def update_reaction_rate(cls, reaction): + ''' + Update the expression of a StochSS Reactions rate parameter + + Attributes + ---------- + reaction : dict + StochSS Reaction + ''' try: if reaction['rate'].keys() and isinstance(reaction['rate']['expression'], str): try: - reaction['rate']['expression'] = ast.literal_eval(reaction['rate']['expression']) + value = ast.literal_eval(reaction['rate']['expression']) + reaction['rate']['expression'] = value except ValueError: pass except KeyError: @@ -133,10 +180,22 @@ def update_reaction_rate(cls, reaction): @classmethod def update_event_assignment(cls, assignment, param_ids): + ''' + Update the target of a StochSS Event Assignment in an event + if its a StochSS Parameter. + + Attributes + ---------- + assignment : dict + StochSS Event Assignment + param_ids : list + list of compIDs for the StochSS parameters collection + ''' try: if assignment['variable']['compID'] in param_ids: try: - assignment['variable']['expression'] = ast.literal_eval(assignment['variable']['expression']) + value = ast.literal_eval(assignment['variable']['expression']) + assignment['variable']['expression'] = value except ValueError: pass except KeyError: @@ -145,6 +204,16 @@ def update_event_assignment(cls, assignment, param_ids): @classmethod def update_event_targets(cls, event, param_ids): + ''' + Update each StochSS Event Assignment in an event. + + Attributes + ---------- + event : dict + StochSS Event + param_ids : list + list of compIDs for the StochSS parameters collection + ''' try: if "eventAssignments" in event.keys(): for assignment in event['eventAssignments']: @@ -155,10 +224,21 @@ def update_event_targets(cls, event, param_ids): @classmethod def update_rule_targets(cls, rule, param_ids): + ''' + Update the target of a StochSS Rule if its a StochSS Parameter. + + Attributes + ---------- + rule : dict + StochSS Rule + param_ids : list + list of compIDs for the StochSS parameters collection + ''' try: if rule['variable']['compID'] in param_ids: try: - rule['variable']['expression'] = ast.literal_eval(rule['variable']['expression']) + value = ast.literal_eval(rule['variable']['expression']) + rule['variable']['expression'] = value except ValueError: pass except KeyError: @@ -167,6 +247,14 @@ def update_rule_targets(cls, rule, param_ids): @classmethod def update_model_data(cls, data): + ''' + Update the expressions in all Parameters, Reactions, Rules, and Events + + Attributes + ---------- + data : dict + StochSS Model + ''' param_ids = [] if "parameters" in data.keys(): for param in data['parameters']: @@ -193,20 +281,21 @@ async def post(self): model_path = self.get_query_argument(name="path") if model_path.startswith('/'): model_path = model_path.replace('/', '', 1) - log.debug("Path to the model: {0}".format(model_path)) - log.debug("Path with escape char spaces: {0}".format(model_path)) + log.debug("Path to the model: %s", model_path) + log.debug("Path with escape char spaces: %s", model_path) full_path = os.path.join('/home/jovyan', model_path) - log.debug("Full path to the model: {0}".format(full_path)) + log.debug("Full path to the model: %s", full_path) data = self.request.body.decode() - log.debug("Model data to be saved: {0}".format(data)) + log.debug("Model data to be saved: %s", data) if os.path.exists(full_path): with open(full_path, 'w') as file: file.write(data) - log.debug("Saved the model: {0}".format(full_path.split('/').pop().split('.')[0])) + log.debug("Saved the model: %s", full_path.split('/').pop().split('.')[0]) else: self.set_status(404) - error = {"Reason":"Model Not Found", "Message":"Could not find the model file: {0}".format(model_path)} - log.error("Exception information: {0}".format(error)) + error = {"Reason":"Model Not Found", + "Message":"Could not find the model file: {0}".format(model_path)} + log.error("Exception information: %s", error) self.write(error) self.finish() @@ -229,27 +318,29 @@ async def get(self): run_cmd = self.get_query_argument(name="cmd") outfile = self.get_query_argument(name="outfile") model_path = self.get_query_argument(name="path") - log.debug("Run command sent to the script: {0}".format(run_cmd)) - log.debug("Path to the model: {0}".format(model_path)) + log.debug("Run command sent to the script: %s", run_cmd) + log.debug("Path to the model: %s", model_path) self.set_header('Content-Type', 'application/json') # Create temporary results file it doesn't already exist if outfile == 'none': outfile = str(uuid.uuid4()).replace("-", "_") - log.debug("Temporary outfile: {0}".format(outfile)) - exec_cmd = ['/stochss/stochss/handlers/util/run_model.py', '{0}'.format(model_path), '{}.tmp'.format(outfile)] # Script commands for read run_cmd + log.debug("Temporary outfile: %s", outfile) + # Script commands for read run_cmd + exec_cmd = ['/stochss/stochss/handlers/util/run_model.py', + '{0}'.format(model_path), '{}.tmp'.format(outfile)] exec_cmd.append(''.join(['--', run_cmd])) - log.debug("Exec command sent to the subprocess: {0}".format(exec_cmd)) + log.debug("Exec command sent to the subprocess: %s", exec_cmd) resp = {"Running":False, "Outfile":outfile, "Results":""} if run_cmd == "start": pipe = subprocess.Popen(exec_cmd) resp['Running'] = True - log.debug("Response to the start command: {0}".format(resp)) + log.debug("Response to the start command: %s", resp) self.write(resp) else: pipe = subprocess.Popen(exec_cmd, stdout=subprocess.PIPE, text=True) results, error = pipe.communicate() - log.debug("Results for the model preview: {0}".format(results)) - log.error("Errors thrown by the subprocess: {0}".format(error)) + log.debug("Results for the model preview: %s", results) + log.error("Errors thrown by the subprocess: %s", error) # Send data back to client if results: resp['Results'] = json.loads(results) @@ -257,7 +348,7 @@ async def get(self): self.set_status(406) else: resp['Running'] = True - log.debug("Response to the read command: {0}".format(resp)) + log.debug("Response to the read command: %s", resp) self.write(resp) self.finish() diff --git a/stochss/handlers/pages.py b/stochss/handlers/pages.py index f5df99782d..b7b185bbb6 100644 --- a/stochss/handlers/pages.py +++ b/stochss/handlers/pages.py @@ -37,6 +37,9 @@ def get_template_path(self): @classmethod def get_server_path(cls): + ''' + Get the stochss server path + ''' try: server_path = os.environ['JUPYTERHUB_SERVICE_PREFIX'] except KeyError: @@ -46,54 +49,108 @@ def get_server_path(cls): class HomeHandler(PageHandler): + ''' + Handler for rendering the stochss hub home page + ''' @web.authenticated async def get(self): + ''' + Render the stochss hub home page + ''' self.render("stochss-home.html", server_path=self.get_server_path()) class UserHomeHandler(PageHandler): + ''' + Handler for rendering the stochss users home page + ''' @web.authenticated async def get(self): + ''' + Render the stochss users home page + ''' self.render("stochss-user-home.html", server_path=self.get_server_path()) class QuickstartHandler(PageHandler): + ''' + Handler for rendering the stochss tutorials page + ''' @web.authenticated async def get(self): + ''' + Render the stochss tutorials page + ''' self.render("stochss-quickstart.html", server_path=self.get_server_path()) class ModelBrowserHandler(PageHandler): + ''' + Handler for rendering the stochss file browser page + ''' @web.authenticated async def get(self): + ''' + Render the stochss file browser page + ''' self.render("stochss-file-browser.html", server_path=self.get_server_path()) class ModelEditorHandler(PageHandler): + ''' + Handler for rendering the stochss model editor page + ''' @web.authenticated async def get(self): + ''' + Render the stochss model editor page + ''' self.render("stochss-model-editor.html", server_path=self.get_server_path()) class WorkflowSelectionHandler(PageHandler): + ''' + Handler for rendering the stochss workflow selection page + ''' @web.authenticated async def get(self): + ''' + Render the stochss workflow selection page + ''' self.render("stochss-workflow-selection.html", server_path=self.get_server_path()) class WorkflowEditorHandler(PageHandler): + ''' + Handler for rendering the stochss workflow manager page + ''' @web.authenticated async def get(self): + ''' + Render the stochss workflow manager page + ''' self.render("stochss-workflow-manager.html", server_path=self.get_server_path()) class ProjectBrowserHandler(PageHandler): + ''' + Handler for rendering the stochss project browser page + ''' @web.authenticated async def get(self): + ''' + Render the stochss project browser page + ''' self.render("stochss-project-browser.html", server_path=self.get_server_path()) class ProjectManagerHandler(PageHandler): + ''' + Handler for rendering the stochss project manager page + ''' @web.authenticated async def get(self): + ''' + Render the stochss project manager page + ''' self.render("stochss-project-manager.html", server_path=self.get_server_path()) diff --git a/stochss/handlers/project.py b/stochss/handlers/project.py index 0ab84cc11b..57834b819b 100644 --- a/stochss/handlers/project.py +++ b/stochss/handlers/project.py @@ -135,37 +135,139 @@ def get(self): @classmethod - def update_model_data(cls, data): - param_ids = [] - for param in data['parameters']: + def update_parameter(cls, param, param_ids): + ''' + Update the expression of a StochSS Parameter + + Attributes + ---------- + param : dict + StochSS Parameter + param_ids : list + list of compIDs for the StochSS parameters collection + ''' + try: param_ids.append(param['compID']) if isinstance(param['expression'], str): try: param['expression'] = ast.literal_eval(param['expression']) except ValueError: pass - for reaction in data['reactions']: + except KeyError: + pass + + + @classmethod + def update_reaction_rate(cls, reaction): + ''' + Update the expression of a StochSS Reactions rate parameter + + Attributes + ---------- + reaction : dict + StochSS Reaction + ''' + try: if reaction['rate'].keys() and isinstance(reaction['rate']['expression'], str): try: value = ast.literal_eval(reaction['rate']['expression']) reaction['rate']['expression'] = value except ValueError: pass - for event in data['eventsCollection']: - for assignment in event['eventAssignments']: - if assignment['variable']['compID'] in param_ids: - try: - value = ast.literal_eval(assignment['variable']['expression']) - assignment['variable']['expression'] = value - except ValueError: - pass - for rule in data['rules']: + except KeyError: + pass + + + @classmethod + def update_event_assignment(cls, assignment, param_ids): + ''' + Update the target of a StochSS Event Assignment in an event + if its a StochSS Parameter. + + Attributes + ---------- + assignment : dict + StochSS Event Assignment + param_ids : list + list of compIDs for the StochSS parameters collection + ''' + try: + if assignment['variable']['compID'] in param_ids: + try: + value = ast.literal_eval(assignment['variable']['expression']) + assignment['variable']['expression'] = value + except ValueError: + pass + except KeyError: + pass + + + @classmethod + def update_event_targets(cls, event, param_ids): + ''' + Update each StochSS Event Assignment in an event. + + Attributes + ---------- + event : dict + StochSS Event + param_ids : list + list of compIDs for the StochSS parameters collection + ''' + try: + if "eventAssignments" in event.keys(): + for assignment in event['eventAssignments']: + cls.update_event_assignment(assignment, param_ids) + except KeyError: + pass + + + @classmethod + def update_rule_targets(cls, rule, param_ids): + ''' + Update the target of a StochSS Rule if its a StochSS Parameter. + + Attributes + ---------- + rule : dict + StochSS Rule + param_ids : list + list of compIDs for the StochSS parameters collection + ''' + try: if rule['variable']['compID'] in param_ids: try: value = ast.literal_eval(rule['variable']['expression']) rule['variable']['expression'] = value except ValueError: pass + except KeyError: + pass + + + @classmethod + def update_model_data(cls, data): + ''' + Update the expressions in all Parameters, Reactions, Rules, and Events + + Attributes + ---------- + data : dict + StochSS Model + ''' + param_ids = [] + if "parameters" in data.keys(): + for param in data['parameters']: + cls.update_parameter(param, param_ids) + if "reactions" in data.keys(): + for reaction in data['reactions']: + cls.update_reaction_rate(reaction) + if "eventsCollection" in data.keys(): + for event in data['eventsCollection']: + cls.update_event_assignment(event, param_ids) + if "rules" in data.keys(): + for rule in data['rules']: + cls.update_rule_targets(rule, param_ids) @classmethod diff --git a/stochss/handlers/util/generate_notebook_cells.py b/stochss/handlers/util/generate_notebook_cells.py index 969c868662..9a7f24f252 100644 --- a/stochss/handlers/util/generate_notebook_cells.py +++ b/stochss/handlers/util/generate_notebook_cells.py @@ -199,9 +199,10 @@ def generate_model_cell(json_data, name): raise Exception('Spatial not yet implemented.') else: # Non-Spatial - if "volume" not in json_data.keys(): + if 'volume' in json_data['modelSettings'].keys(): json_data['volume'] = json_data['modelSettings']['volume'] - + elif "volume" not in json_data.keys(): + json_data['volume'] = 1 model_cell += 'class {0}(Model):\n'.format(name) model_cell += ' def __init__(self, parameter_values=None):\n' padding = ' ' diff --git a/stochss/handlers/util/model_exploration.py b/stochss/handlers/util/model_exploration.py new file mode 100644 index 0000000000..c345b576dd --- /dev/null +++ b/stochss/handlers/util/model_exploration.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +''' +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2020 StochSS developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' + +import os +import json + +from stochss.handlers.util.stochss_errors import StochSSPermissionsError + +class ModelExploration(): + + def __init__(self, wkfl_path, mdl_path, settings=None): + self.wkfl_path = wkfl_path + self.mdl_path = mdl_path + self.settings = self.get_settings() if settings is None else settings + self.mdl_file = mdl_path.split('/').pop() + self.info_path = os.path.join(wkfl_path, 'info.json') + self.log_path = os.path.join(wkfl_path, 'logs.txt') + self.wkfl_mdl_path = os.path.join(wkfl_path, self.mdl_file) + self.res_path = os.path.join(wkfl_path, 'results') + wkfl_name_elements = wkfl_path.split('/').pop().split('.')[0].split('_') + try: + date, time = wkfl_name_elements[-2:] + if date.isdigit() and time.isdigit(): + self.wkfl_timestamp = '_'.join(["",date,time]) + else: + self.wkfl_timestamp = None + except: + self.wkfl_timestamp = None + + + def get_settings(self): + settings_path = os.path.join(self.wkfl_path, "settings.json") + + if os.path.exists(settings_path): + with open(settings_path, "r") as settings_file: + return json.load(settings_file) + + with open("/stochss/stochss_templates/workflowSettingsTemplate.json", "r") as template_file: + settings_template = json.load(template_file) + + if os.path.exists(self.wkfl_mdl_path): + with open(self.wkfl_mdl_path, "r") as mdl_file: + mdl = json.load(mdl_file) + try: + settings = {"simulationSettings":mdl['simulationSettings'], + "parameterSweepSettings":mdl['parameterSweepSettings'], + "modelExplarationSettings":settings_template['modelExplorationSettings'], + "resultsSettings":settings_template['resultsSettings']} + return settings + except: + return settings_template + else: + return settings_template + + + def save(self): + settings_path = os.path.join(self.wkfl_path, "settings.json") + with open(settings_path, "w") as settings_file: + json.dump(self.settings, settings_file) + + + def run(self, gillespy2_model, verbose): + message = "StochSS Model Exploration Jobs are currently not supported" + raise StochSSPermissionsError(message) diff --git a/stochss/handlers/util/parameter_sweep.py b/stochss/handlers/util/parameter_sweep.py index af03e21414..ecbb3f089d 100755 --- a/stochss/handlers/util/parameter_sweep.py +++ b/stochss/handlers/util/parameter_sweep.py @@ -439,6 +439,7 @@ def get_settings(self): try: settings = {"simulationSettings":mdl['simulationSettings'], "parameterSweepSettings":mdl['parameterSweepSettings'], + "modelExplarationSettings":settings_template['modelExplorationSettings'], "resultsSettings":settings_template['resultsSettings']} return settings except: diff --git a/stochss/handlers/util/run_model.py b/stochss/handlers/util/run_model.py index e8cf8010fd..f01ca5ab61 100755 --- a/stochss/handlers/util/run_model.py +++ b/stochss/handlers/util/run_model.py @@ -87,6 +87,7 @@ def get_settings(self): try: settings = {"simulationSettings":mdl['simulationSettings'], "parameterSweepSettings":mdl['parameterSweepSettings'], + "modelExplarationSettings":settings_template['modelExplorationSettings'], "resultsSettings":settings_template['resultsSettings']} return settings except: @@ -239,10 +240,12 @@ def __init__(self, data, is_ode): name = data['name'] timeStep = (data['modelSettings']['timeStep']) endSim = data['modelSettings']['endSim'] - if "volume" not in data.keys(): + if "volume" in data.keys(): + volume = data['volume'] + elif "volume" in data['modelSettings'].keys(): volume = data['modelSettings']['volume'] else: - volume = data['volume'] + volume = 1 self.species = list(map(lambda s: self.build_specie(s, is_ode), data['species'])) self.parameters = list(map(lambda p: self.build_parameter(p), data['parameters'])) self.reactions = list(map(lambda r: self.build_reaction(r, self.parameters), data['reactions'])) diff --git a/stochss/handlers/util/run_workflow.py b/stochss/handlers/util/run_workflow.py index 45ca764a1b..f8ebc8c12a 100755 --- a/stochss/handlers/util/run_workflow.py +++ b/stochss/handlers/util/run_workflow.py @@ -31,12 +31,9 @@ from datetime import datetime, timezone, timedelta from gillespy2.core import log -try: - from parameter_sweep import ParameterSweep - from run_model import GillesPy2Workflow, ModelFactory, run_solver -except ModuleNotFoundError: - from .parameter_sweep import ParameterSweep - from .run_model import GillesPy2Workflow, ModelFactory, run_solver +from stochss.handlers.util.parameter_sweep import ParameterSweep +from stochss.handlers.util.run_model import GillesPy2Workflow, ModelFactory, run_solver +from stochss.handlers.util.model_exploration import ModelExploration def save_new_workflow(wkfl, wkfl_type, initialize): @@ -238,7 +235,8 @@ def initialize(mdl_path, wkfl_path, wkfl_type, settings=None, new=False, existin if not workflow_name in os.listdir(path=dir_path): os.mkdir(workflow_path) # make the workflow directory - workflows = {"gillespy":GillesPy2Workflow, "parameterSweep":ParameterSweep} + workflows = {"gillespy":GillesPy2Workflow, "parameterSweep":ParameterSweep, + "modelExploration":ModelExploration} workflow = workflows[wkfl_type](workflow_path, model_path, settings) diff --git a/stochss/handlers/workflows.py b/stochss/handlers/workflows.py index 02ad421f33..0b199a613f 100644 --- a/stochss/handlers/workflows.py +++ b/stochss/handlers/workflows.py @@ -64,8 +64,8 @@ async def get(self): log.debug("Time stamp of the workflow: %s", stamp) log.debug("The type of the workflow: %s", wkfl_type) log.debug("The path to the workflow/model: %s", path) - title_types = {"gillespy":"Ensemble Simulation", "parameterSweep":"Parameter Sweep"} - name_types = {"gillespy":"_ES", "parameterSweep":"_PS"} + title_types = {"gillespy":"Ensemble Simulation", "parameterSweep":"Parameter Sweep", "modelExploration":"Model Exploration"} + name_types = {"gillespy":"_ES", "parameterSweep":"_PS", "modelExploration":"_ME"} parent_path = self.get_query_argument(name='parentPath', default=os.path.dirname(path)) if path.endswith('.mdl'): resp = {"mdlPath":path, "timeStamp":stamp, "type":wkfl_type, diff --git a/stochss_templates/workflowSettingsTemplate.json b/stochss_templates/workflowSettingsTemplate.json index fe88916077..fd33153cc3 100644 --- a/stochss_templates/workflowSettingsTemplate.json +++ b/stochss_templates/workflowSettingsTemplate.json @@ -20,6 +20,10 @@ "parameterTwo": {}, "speciesOfInterest": {} }, + "modelExplorationSettings":{ + "distributionType":"Uniform", + "variables": [] + }, "resultsSettings":{ "mapper":"final", "reducer":"avg",