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",