From cc1187fc7d8f0ae2d1340689279ca0083466cd51 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 4 Jun 2021 13:00:53 -0400 Subject: [PATCH 001/133] Added boundary conditions to the spatial model converter. --- .../handlers/util/stochss_spatial_model.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index e0f444ca0e..5e854c884a 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -23,7 +23,7 @@ import numpy import plotly -from spatialpy import Model, Species, Parameter, Reaction, Mesh, MeshError, \ +from spatialpy import Model, Species, Parameter, Reaction, Mesh, MeshError, BoundaryCondition, \ PlaceInitialCondition, UniformInitialCondition, ScatterInitialCondition from .stochss_base import StochSSBase @@ -69,6 +69,23 @@ def __init__(self, path, new=False, model=None): self.model = None + @classmethod + def __build_boundary_condition(cls, boundary_condition): + class NewBC(BoundaryCondition): # pylint: disable=too-few-public-methods + ''' + ######################################################################################## + Custom SpatialPy Boundary Condition + ######################################################################################## + ''' + __class__ = f"__main__.{boundary_condition['name']}" + def expression(self): # pylint: disable=no-self-use + ''' + Custom expression for boundary condition + ''' + return boundary_condition['expression'] + return NewBC() + + @classmethod def __build_stochss_domain(cls, s_domain, data=None): particles = cls.__build_stochss_domain_particles(s_domain=s_domain, data=data) @@ -121,6 +138,17 @@ def __build_stochss_domain_particles(cls, s_domain, data=None): return particles + def __convert_boundary_conditions(self, model): + try: + for boundary_condition in self.model['boundaryConditions']: + s_bound_cond = self.__build_boundary_condition(boundary_condition) + model.add_boundary_condition(s_bound_cond) + except KeyError as err: + message = "Spatial model boundary conditions are not properly formatted or " + message += f"are referenced incorrectly: {str(err)}" + raise StochSSModelFormatError(message, traceback.format_exc()) from err + + def __convert_domain(self, model): try: xlim = tuple(self.model['domain']['x_lim']) @@ -161,7 +189,7 @@ def __convert_initial_conditions(self, model): s_ic = UniformInitialCondition(species, count, types=types) model.add_initial_condition(s_ic) except KeyError as err: - message = "Spatial model domain properties are not properly formatted or " + message = "Spatial model initial conditions are not properly formatted or " message += f"are referenced incorrectly: {str(err)}" raise StochSSModelFormatError(message, traceback.format_exc()) from err @@ -350,6 +378,8 @@ def convert_to_spatialpy(self): s_model = Model(name=name) self.__convert_model_settings(model=s_model) self.__convert_domain(model=s_model) + if "boundaryConditions" in self.model.keys(): + self.__convert_boundary_conditions(model=s_model) self.__convert_species(model=s_model) self.__convert_initial_conditions(model=s_model) self.__convert_parameters(model=s_model) From 48fa06d80c5b95fd052147061bebd3835cec720f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 13:02:48 -0400 Subject: [PATCH 002/133] Added boundary conditions to the notebook template for spatial models. --- stochss/handlers/util/stochss_notebook.py | 40 +++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/stochss/handlers/util/stochss_notebook.py b/stochss/handlers/util/stochss_notebook.py index 2a02836cd8..bd3db26214 100644 --- a/stochss/handlers/util/stochss_notebook.py +++ b/stochss/handlers/util/stochss_notebook.py @@ -72,6 +72,34 @@ def __init__(self, path, new=False, models=None, settings=None): if changed: self.path = n_path.replace(self.user_dir + '/', "") + def __create_boundary_condition_cells(self): + pad = " " + bc_cells = [] + try: + for boundary_condition in self.s_model['boundaryConditions']: + bc_cell = [f'class {boundary_condition["name"]}(BoundaryCondition):', + f'{pad}def expression(self):', + f'{pad*2}return """{boundary_condition["expression"]}"""'] + bc_cells.append(nbf.new_code_cell("\n".join(bc_cell))) + return bc_cells + except KeyError as err: + message = "Boundary conditions are not properly formatted or " + message += f"are referenced incorrectly for notebooks: {str(err)}" + raise StochSSModelFormatError(message, traceback.format_exc()) from err + + def __create_boundary_condition_string(self, model, pad): + if self.s_model['boundaryConditions']: + bound_conds = ["", f"{pad}# Boundary Conditions"] + try: + for bound_cond in self.s_model['boundaryConditions']: + bc_str = f"{pad}self.add_boundary_condition({bound_cond['name']}())" + bound_conds.append(bc_str) + model.extend(bound_conds) + except KeyError as err: + message = "Boundary conditions are not properly formatted or " + message += f"are referenced incorrectly for notebooks: {str(err)}" + raise StochSSModelFormatError(message, traceback.format_exc()) from err + def __create_common_cells(self, interactive_backend=False): cells = [self.__create_import_cell(interactive_backend=interactive_backend), nbf.new_markdown_cell(f"# {self.get_name()}"), @@ -261,6 +289,7 @@ def __create_model_cell(self): " def __init__(self):", f'{pad}Model.__init__(self, name="{self.get_name()}")'] self.__create_mesh_string(model=model, pad=pad) + self.__create_boundary_condition_string(model=model, pad=pad) self.__create_species_strings(model=model, pad=pad) self.__create_initial_condition_strings(model=model, pad=pad) self.__create_parameter_strings(model=model, pad=pad) @@ -905,9 +934,16 @@ def create_ses_notebook(self): self.nb_type = self.SPATIAL_SIMULATION self.settings['solver'] = "Solver" run_str = "kwargs = configure_simulation()\nresults = model.run(**kwargs)" - species = self.s_model['species'][0]['name'] - plot_str = f"results.plot_species('{species}', animated=True, width=None, height=None)" + if self.s_model['species']: + species = self.s_model['species'][0]['name'] + plot_str = f"results.plot_species('{species}', animated=True, width=None, height=None)" + else: + plot_str = f"results.plot_property('type', animated=True, width=None, height=None)" cells = self.__create_common_cells() + if 'boundaryConditions' in self.s_model.keys(): + bc_cells = self.__create_boundary_condition_cells() + for i, bc_cell in enumerate(bc_cells): + cells.insert(2 + i, bc_cell) cells.extend([nbf.new_code_cell(run_str), nbf.new_markdown_cell("# Visualization"), nbf.new_code_cell(plot_str)]) From 0379792f5876408e67301c9ac7be8778c7c21414 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 13:04:29 -0400 Subject: [PATCH 003/133] Added boundary conditions to the front end model to prevent unwanted deletion when saving the model. --- client/models/boundary-condition.js | 29 ++++++++++++++++++++++++++++ client/models/boundary-conditions.js | 26 +++++++++++++++++++++++++ client/models/model.js | 4 +++- 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 client/models/boundary-condition.js create mode 100644 client/models/boundary-conditions.js diff --git a/client/models/boundary-condition.js b/client/models/boundary-condition.js new file mode 100644 index 0000000000..405d84e800 --- /dev/null +++ b/client/models/boundary-condition.js @@ -0,0 +1,29 @@ +/* +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: { + compID: 'number', + name: 'string', + expression: 'string', + annotation: 'string' + } +}); \ No newline at end of file diff --git a/client/models/boundary-conditions.js b/client/models/boundary-conditions.js new file mode 100644 index 0000000000..aeed723ffe --- /dev/null +++ b/client/models/boundary-conditions.js @@ -0,0 +1,26 @@ +/* +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 . +*/ + +//collections +var Collection = require('ampersand-collection'); +//models +var BoundaryCondition = require('./boundary-condition'); + +module.exports = Collection.extend({ + model: BoundaryCondition, +}); \ No newline at end of file diff --git a/client/models/model.js b/client/models/model.js index b8b59ab441..d5ca4c628c 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -30,6 +30,7 @@ var Reactions = require('./reactions'); var Rules = require('./rules'); var Events = require('./events'); var FunctionDefinitions = require('./function-definitions'); +var BoundaryCondition = require('./boundary-conditions'); module.exports = Model.extend({ url: function () { @@ -49,7 +50,8 @@ module.exports = Model.extend({ reactions: Reactions, rules: Rules, eventsCollection: Events, - functionDefinitions: FunctionDefinitions + functionDefinitions: FunctionDefinitions, + boundaryConditions: BoundaryCondition }, children: { modelSettings: TimespanSettings, From 33e0cacf45cec895cede25731d28af0f655699cc Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 13:07:42 -0400 Subject: [PATCH 004/133] Added a view for a boundary condition model with a view and edit template to support both modes. --- .../includes/editBoundaryCondition.pug | 30 +++++++ .../includes/viewBoundaryCondition.pug | 18 ++++ client/views/boundary-condition-view.js | 85 +++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 client/templates/includes/editBoundaryCondition.pug create mode 100644 client/templates/includes/viewBoundaryCondition.pug create mode 100644 client/views/boundary-condition-view.js diff --git a/client/templates/includes/editBoundaryCondition.pug b/client/templates/includes/editBoundaryCondition.pug new file mode 100644 index 0000000000..2caa182c6f --- /dev/null +++ b/client/templates/includes/editBoundaryCondition.pug @@ -0,0 +1,30 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-md-3 + + div.pl-2=this.model.name + + div.col-md-5 + + div + + button.btn.btn-outline-secondary(data-hook="expand") View Expression + + div(data-hook="expression" style="display: none;") + + div(style="white-space:pre;")=this.model.expression + + div.col-md-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm(data-hook="edit-annotation-btn") Edit + + div.col-md-2 + + button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/viewBoundaryCondition.pug b/client/templates/includes/viewBoundaryCondition.pug new file mode 100644 index 0000000000..a78f94d10b --- /dev/null +++ b/client/templates/includes/viewBoundaryCondition.pug @@ -0,0 +1,18 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-md-3 + + div.pl-2=this.model.name + + div.col-md-7 + + div(style="white-space:pre;")=this.model.expression + + div.col-md-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/views/boundary-condition-view.js b/client/views/boundary-condition-view.js new file mode 100644 index 0000000000..6c88802b95 --- /dev/null +++ b/client/views/boundary-condition-view.js @@ -0,0 +1,85 @@ +/* +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 . +*/ + +let $ = require('jquery'); +//support files +let modals = require('../modals'); +//views +let View = require('ampersand-view'); +//templates +let editTemplate = require('../templates/includes/editBoundaryCondition.pug'); +let viewTemplate = require('../templates/includes/viewBoundaryCondition.pug'); + +module.exports = View.extend({ + events: { + 'click [data-hook=expand]' : 'toggleExpressionView', + 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', + 'click [data-hook=remove]' : 'removeBoundaryCondition' + }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false + }, + render: function (attrs, options) { + this.template = this.viewMode ? viewTemplate : editTemplate; + View.prototype.render.apply(this, arguments); + $(document).on('shown.bs.modal', function (e) { + $('[autofocus]', e.target).focus(); + }); + $(document).on('hide.bs.modal', '.modal', function (e) { + e.target.remove() + }); + if(!this.model.annotation){ + $(this.queryByHook('edit-annotation-btn')).text('Add') + } + }, + editAnnotation: function () { + let self = this; + let name = this.model.name; + let annotation = this.model.annotation; + if(document.querySelector('#boundaryConditionAnnotationModal')) { + document.querySelector('#boundaryConditionAnnotationModal').remove(); + } + let modal = $(modals.annotationModalHtml("boundaryCondition", name, annotation)).modal(); + let okBtn = document.querySelector('#boundaryConditionAnnotationModal .ok-model-btn'); + let input = document.querySelector('#boundaryConditionAnnotationModal #boundaryConditionAnnotationInput'); + input.addEventListener("keyup", function (event) { + if(event.keyCode === 13){ + event.preventDefault(); + okBtn.click(); + } + }); + okBtn.addEventListener('click', function (e) { + self.model.annotation = input.value.trim(); + self.parent.renderBoundaryConditionView(); + modal.modal('hide'); + }); + }, + removeBoundaryCondition: function () { + this.model.collection.remove(this.model); + }, + toggleExpressionView: function (e) { + if(e.target.textContent.startsWith("View")){ + $(this.queryByHook("expand")).html("Hide Expression"); + $(this.queryByHook("expression")).css("display", "block"); + }else{ + $(this.queryByHook("expand")).html("View Expression"); + $(this.queryByHook("expression")).css("display", "none"); + } + } +}); \ No newline at end of file From 51f5ce15a12609fbc23fa8d12d3a08d2f055f4ce Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 13:09:29 -0400 Subject: [PATCH 005/133] Added a view for the boundary conditions collection that supports a read only mode for model viewers. --- .../includes/boundaryConditionsEditor.pug | 59 ++++++++++++++ client/views/boundary-conditions-editor.js | 78 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 client/templates/includes/boundaryConditionsEditor.pug create mode 100644 client/views/boundary-conditions-editor.js diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug new file mode 100644 index 0000000000..984a21dcb2 --- /dev/null +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -0,0 +1,59 @@ +div#boundary-conditions.card + + div.card-header.pb-0 + + h3.inline.mr-3 Boundary Conditions + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="bc-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="bc-edit-tab" data-toggle="tab" href="#edit-boundary-conditions") Edit + + li.nav-item + + a.nav-link.tab(data-hook="bc-view-tab" data-toggle="tab" href="#view-boundary-conditions") View + + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-boundary-conditions", data-hook="collapse") + + + div.card-body.collapse.tab-content(id="collapse-boundary-conditions") + + div.tab-pane.active(id="edit-boundary-conditions" data-hook="edit-boundary-conditions") + + p Set spatial regions of the domain where a property of particles are held constant. + + hr + + div.mx-1.row.head + + div.col-sm-3: h6 Name + + div.col-sm-5: h6 Expression + + div.col-sm-2 + + div.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-sm-2: h6 Remove + + div.mt-3(data-hook="edit-boundary-conditions") + + div.tab-pane(id="view-boundary-conditions" data-hook="view-boundary-conditions") + + div.mx-1.row.head + + div.col-sm-3: h6 Name + + div.col-sm-7: h6 Expression + + div.col-sm-2 + + div.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.mt-3(data-hook="view-boundary-conditions") diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js new file mode 100644 index 0000000000..142eb3d425 --- /dev/null +++ b/client/views/boundary-conditions-editor.js @@ -0,0 +1,78 @@ +/* +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 . +*/ + +let $ = require('jquery'); +//support files +let Tooltips = require('../tooltips'); +//views +let View = require('ampersand-view'); +let BoundaryConditionView = require('./boundary-condition-view'); +//templates +let template = require('../templates/includes/boundaryConditionsEditor.pug'); + +module.exports = View.extend({ + template: template, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.tooltips = Tooltips.boundaryConditionsEditor; + }, + render: function (attrs, options) { + View.prototype.render.apply(this, arguments); + if(this.readOnly) { + $(this.queryByHook('bc-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('bc-view-tab')).tab('show'); + $(this.queryByHook('edit-boundary-conditions')).removeClass('active'); + $(this.queryByHook('view-boundary-conditions')).addClass('active'); + } + this.renderEditBoundaryConditionView(); + this.renderViewBoundaryConditionView(); + }, + renderEditBoundaryConditionView: function () { + if(this.editBoundaryConditionView) { + this.editBoundaryConditionView.remove(); + } + this.editBoundaryConditionView = this.renderCollection( + this.collection, + BoundaryConditionView, + this.queryByHook("edit-boundary-conditions") + ); + $(document).ready(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewBoundaryConditionView: function () { + if(this.viewBoundaryConditionView) { + this.viewBoundaryConditionView.remove(); + } + let options = {viewOptions: {viewMode: true}}; + this.viewBoundaryConditionView = this.renderCollection( + this.collection, + BoundaryConditionView, + this.queryByHook("view-boundary-conditions"), + options + ); + } +}); \ No newline at end of file From 6ec993108dff1007c2e93b649047da0e1d7d4bf3 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 13:10:29 -0400 Subject: [PATCH 006/133] Added boundary condition tooltips to the tooltips file. --- client/tooltips.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/tooltips.js b/client/tooltips.js index 4c426230a2..00cc144e9b 100644 --- a/client/tooltips.js +++ b/client/tooltips.js @@ -178,5 +178,8 @@ module.exports = { pressure: "Atmospheric or background pressure.", speed: "Approximate or artificial speed of sound" + }, + boundaryConditionsEditor: { + annotation: "An optional note about a boundary condition." } } From 9d5af2a439b7539af96c2a88bdbf8224e07c1976 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 13:11:34 -0400 Subject: [PATCH 007/133] Added the boundary conditions section to the model editor page. --- client/pages/model-editor.js | 17 ++++++++++++++++- client/templates/pages/modelEditor.pug | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index ce70b0177e..959c6a4037 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -44,6 +44,7 @@ var SBMLComponentView = require('../views/sbml-component-editor'); var TimespanSettingsView = require('../views/timespan-settings'); var ModelStateButtonsView = require('../views/model-state-buttons'); var QuickviewDomainTypes = require('../views/quickview-domain-types'); +let BoundaryConditionsView = require('../views/boundary-conditions-editor'); //models var Model = require('../models/model'); var Domain = require('../models/domain'); @@ -232,7 +233,12 @@ let ModelEditor = PageView.extend({ app.registerRenderSubview(this, this.modelSettings, 'model-settings-container'); app.registerRenderSubview(this, this.modelStateButtons, 'model-state-buttons-container'); if(this.model.is_spatial) { - $(this.queryByHook("model-editor-advanced-container")).css("display", "none"); + if(this.model.boundaryConditions && this.model.boundaryConditions.length) { + $(this.queryByHook("system-volume-container")).css("display", "none"); + this.renderBoundaryConditionsView(); + }else{ + $(this.queryByHook("model-editor-advanced-container")).css("display", "none"); + } $(this.queryByHook("spatial-beta-message")).css("display", "block"); this.renderDomainViewer(); this.renderInitialConditions(); @@ -260,6 +266,15 @@ let ModelEditor = PageView.extend({ e.target.remove() }); }, + renderBoundaryConditionsView: function() { + if(this.boundaryConditionsView) { + this.boundaryConditionsView.remove(); + } + this.boundaryConditionsView = new BoundaryConditionsView({ + collection: this.model.boundaryConditions + }); + app.registerRenderSubview(this, this.boundaryConditionsView, "boundary-conditions-container"); + }, renderDomainViewer: function(domainPath=null) { if(this.domainViewer) { this.domainViewer.remove() diff --git a/client/templates/pages/modelEditor.pug b/client/templates/pages/modelEditor.pug index eeb3660d85..6d9dda37ee 100644 --- a/client/templates/pages/modelEditor.pug +++ b/client/templates/pages/modelEditor.pug @@ -52,6 +52,8 @@ section.page div(data-hook="sbml-component-container") + div(data-hook="boundary-conditions-container") + div.card.card-body.collapse.show(data-hook="system-volume-container") div From 6437f2759074ddc97693ed00d26e05c4fa6025f0 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 7 Jun 2021 17:24:18 -0400 Subject: [PATCH 008/133] Started setting up a base boundary conditions constructor ui to allow users to build restricted boundary conditions. --- .../includes/boundaryConditionsEditor.pug | 73 ++++++- client/views/boundary-conditions-editor.js | 198 +++++++++++++++++- 2 files changed, 265 insertions(+), 6 deletions(-) diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug index 984a21dcb2..57efbede60 100644 --- a/client/templates/includes/boundaryConditionsEditor.pug +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -16,7 +16,7 @@ div#boundary-conditions.card a.nav-link.tab(data-hook="bc-view-tab" data-toggle="tab" href="#view-boundary-conditions") View - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-boundary-conditions", data-hook="collapse") + + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-boundary-conditions", data-hook="collapse-bc") + div.card-body.collapse.tab-content(id="collapse-boundary-conditions") @@ -40,7 +40,74 @@ div#boundary-conditions.card div.col-sm-2: h6 Remove - div.mt-3(data-hook="edit-boundary-conditions") + div.mt-3(data-hook="edit-boundary-conditions-list") + + div.card.mt-3 + + div.card-header + + h4.inline New Boundary Condition + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-new-boundary-conditions" data-hook="collapse-new-bc") + + + div.card-body.mx-1.collapse(id="collapse-new-boundary-conditions") + + div.row.head + + div.col-sm-2: h6 Name + + div.col-sm-2: h6 Target + + div.col-sm-2: h6 Value + + div.col-sm-6: h6 Conditions + + div.row.mt-3 + + div.col-sm-2(data-hook="new-bc-name" data-target="name") + + div.col-sm-4 + + table.table + tbody + tr + th(scope="row") Target + td(data-hook="new-bc-target") + tr + th(scope="row") Value + td(data-hook="new-bc-value" data-target="value") + tr + th(scope="row") Deterministic + td + input(type="checkbox" checked=this.newBC.deterministic data-hook="new-bc-deterministic") + + div.col-sm-6 + + div + + span.inline.mr-3(for="new-bc-type") Type: + div.inline(id="new-bc-type" data-hook="new-bc-type" data-target="type_id") + + table.table + thead + tr + th(scope="col") + th(scope="col") X-Axis + th(scope="col") Y-Axis + th(scope="col") Z-Axis + tbody + tr + th(scope="row") Min + td: div(data-hook="new-bc-x-min" data-target="xmin") + td: div(data-hook="new-bc-y-min" data-target="ymin") + td: div(data-hook="new-bc-z-min" data-target="zmin") + tr + th(scope="row") Max + td: div(data-hook="new-bc-x-max" data-target="xmax") + td: div(data-hook="new-bc-y-max" data-target="ymax") + td: div(data-hook="new-bc-z-max" data-target="zmax") + + button.btn.btn-outline-primary.box-shadow(data-hook="add-new-bc") Add New Boundary Condition div.tab-pane(id="view-boundary-conditions" data-hook="view-boundary-conditions") @@ -56,4 +123,4 @@ div#boundary-conditions.card div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - div.mt-3(data-hook="view-boundary-conditions") + div.mt-3(data-hook="view-boundary-conditions-list") diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index 142eb3d425..d2214e7695 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -18,8 +18,11 @@ along with this program. If not, see . let $ = require('jquery'); //support files +let app = require('../app'); +let tests = require('./tests'); let Tooltips = require('../tooltips'); //views +let InputView = require('./input'); let View = require('ampersand-view'); let BoundaryConditionView = require('./boundary-condition-view'); //templates @@ -27,10 +30,25 @@ let template = require('../templates/includes/boundaryConditionsEditor.pug'); module.exports = View.extend({ template: template, + events: { + 'change [data-hook=new-bc-name]' : 'handleSetValue', + 'change [data-hook=new-bc-type]' : 'handleSetValue', + 'change [data-hook=new-bc-value]' : 'handleSetValue', + 'change [data-hook=new-bc-x-min]' : 'handleSetValue', + 'change [data-hook=new-bc-x-max]' : 'handleSetValue', + 'change [data-hook=new-bc-y-min]' : 'handleSetValue', + 'change [data-hook=new-bc-y-max]' : 'handleSetValue', + 'change [data-hook=new-bc-z-min]' : 'handleSetValue', + 'change [data-hook=new-bc-z-max]' : 'handleSetValue', + 'click [data-hook=collapse-bc]' : 'changeCollapseButtonText', + 'click [data-hook=collapse-new-bc' : 'changeCollapseButtonText', + 'click [data-hook=add-new-bc]' : 'handleAddBCClick' + }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.boundaryConditionsEditor; + this.setDefaultBC(); }, render: function (attrs, options) { View.prototype.render.apply(this, arguments); @@ -43,10 +61,31 @@ module.exports = View.extend({ $(this.queryByHook('bc-view-tab')).tab('show'); $(this.queryByHook('edit-boundary-conditions')).removeClass('active'); $(this.queryByHook('view-boundary-conditions')).addClass('active'); + }else{ + this.renderEditBoundaryConditionView(); + this.toggleAddNewBCButton(); } - this.renderEditBoundaryConditionView(); this.renderViewBoundaryConditionView(); }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, + handleAddBCClick: function (e) { + let newBC = JSON.stringify(this.newBC) + this.setDefaultBC(); + this.resetNewBCViews(); + }, + handleSetValue: function (e) { + let key = e.delegateTarget.dataset.target; + let value = e.target.value; + if(key.endsWith("min") || key.endsWith("max") || key === "type_id"){ + value = this.validateNewBCCondition(key, value); + }else if(key === "value" && value === "") { + value = null; + } + this.newBC[key] = value; + this.toggleAddNewBCButton(); + }, renderEditBoundaryConditionView: function () { if(this.editBoundaryConditionView) { this.editBoundaryConditionView.remove(); @@ -54,7 +93,7 @@ module.exports = View.extend({ this.editBoundaryConditionView = this.renderCollection( this.collection, BoundaryConditionView, - this.queryByHook("edit-boundary-conditions") + this.queryByHook("edit-boundary-conditions-list") ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); @@ -71,8 +110,161 @@ module.exports = View.extend({ this.viewBoundaryConditionView = this.renderCollection( this.collection, BoundaryConditionView, - this.queryByHook("view-boundary-conditions"), + this.queryByHook("view-boundary-conditions-list"), options ); + }, + resetNewBCViews: function () { + $(this.queryByHook("new-bc-name")).find("input").val(this.newBC.name); + $(this.queryByHook("new-bc-type")).find("input").val(this.newBC.type_id); + $(this.queryByHook("new-bc-value")).find("input").val(this.newBC.value); + $(this.queryByHook("new-bc-x-min")).find("input").val(this.newBC.xmin); + $(this.queryByHook("new-bc-x-max")).find("input").val(this.newBC.xmax); + $(this.queryByHook("new-bc-y-min")).find("input").val(this.newBC.ymin); + $(this.queryByHook("new-bc-y-max")).find("input").val(this.newBC.ymax); + $(this.queryByHook("new-bc-z-min")).find("input").val(this.newBC.zmin); + $(this.queryByHook("new-bc-z-max")).find("input").val(this.newBC.zmax); + this.toggleAddNewBCButton(); + }, + setDefaultBC: function () { + this.newBC = {"name": "", "species": null, "property": null, "value": null, "deterministic": true, "type_id": null, + "xmin": null, "ymin": null, "zmin": null, "xmax": null, "ymax": null, "zmax": null}; + this.setConditions = []; + }, + toggleAddNewBCButton: function () { + let disabled = this.newBC.name === "" || this.newBC.value === null || !this.setConditions.length; + $(this.queryByHook("add-new-bc")).prop("disabled", disabled); + }, + update: function (e) {}, + validateNewBCCondition: function(key, value) { + if((value === 0 && key === "type_id") || value === "") { + value = null + if(this.setConditions.includes(key)){ + let index = this.setConditions.indexOf(key); + this.setConditions.splice(index, 1); + } + }else if(!this.setConditions.includes(key)){ + this.setConditions.push(key); + } + return value; + }, + subviews: { + newBCName: { + hook: "new-bc-name", + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'name', + tests: tests.nameTests, + valueType: 'string', + value: this.newBC.name, + }); + } + }, + newBCValue: { + hook: "new-bc-value", + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'value', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.value, + }); + } + }, + newBCxmin: { + hook: "new-bc-x-min", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'xmin', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.xmin, + }); + } + }, + newBCymin: { + hook: "new-bc-y-min", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'ymin', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.ymin, + }); + } + }, + newBCzmin: { + hook: "new-bc-z-min", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'zmin', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.zmin, + }); + } + }, + newBCxmax: { + hook: "new-bc-x-max", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'xmax', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.xmax, + }); + } + }, + newBCymax: { + hook: "new-bc-y-max", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'ymax', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.ymax, + }); + } + }, + newBCzmax: { + hook: "new-bc-z-max", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'zmax', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.zmax, + }); + } + }, + newBCType: { + hook: "new-bc-type", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'type', + tests: tests.valueTests, + valueType: 'number', + value: this.newBC.type_id, + }); + } + } } }); \ No newline at end of file From ce11aafaa2a01101eb52ee486d7e25fd5bada71b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 10:47:18 -0400 Subject: [PATCH 009/133] Added check to add boundary conditions list if missing. --- stochss/handlers/util/stochss_spatial_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index 5293171c84..c95183c718 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -559,6 +559,8 @@ def load(self): self.model['domain'] = self.get_model_template()['domain'] elif "static" not in self.model['domain'].keys(): self.model['domain']['static'] = True + if "boundaryConditions" not in self.model.keys(): + self.model['boundaryConditions'] = [] for species in self.model['species']: if "types" not in species.keys(): species['types'] = list(range(1, len(self.model['domain']['types']))) From cbcda234a63d1d9342bdf76ee64db60d573f1602 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 10:49:39 -0400 Subject: [PATCH 010/133] Set the model editor to always render the boundary conditions section for spatial models. --- client/pages/model-editor.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 959c6a4037..94b3838b79 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -233,12 +233,8 @@ let ModelEditor = PageView.extend({ app.registerRenderSubview(this, this.modelSettings, 'model-settings-container'); app.registerRenderSubview(this, this.modelStateButtons, 'model-state-buttons-container'); if(this.model.is_spatial) { - if(this.model.boundaryConditions && this.model.boundaryConditions.length) { - $(this.queryByHook("system-volume-container")).css("display", "none"); - this.renderBoundaryConditionsView(); - }else{ - $(this.queryByHook("model-editor-advanced-container")).css("display", "none"); - } + $(this.queryByHook("system-volume-container")).css("display", "none"); + this.renderBoundaryConditionsView(); $(this.queryByHook("spatial-beta-message")).css("display", "block"); this.renderDomainViewer(); this.renderInitialConditions(); From e6a4197b93388402349cb05d8eaf760498f3a65a Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 10:56:54 -0400 Subject: [PATCH 011/133] Corrected the labels and headers in the new boundary condition dection and disabled the concentration input by default. --- client/templates/includes/boundaryConditionsEditor.pug | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug index 57efbede60..8cfddaea37 100644 --- a/client/templates/includes/boundaryConditionsEditor.pug +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -56,9 +56,7 @@ div#boundary-conditions.card div.col-sm-2: h6 Name - div.col-sm-2: h6 Target - - div.col-sm-2: h6 Value + div.col-sm-4: h6 Target div.col-sm-6: h6 Conditions @@ -71,15 +69,15 @@ div#boundary-conditions.card table.table tbody tr - th(scope="row") Target + th(scope="row") Property/Variable td(data-hook="new-bc-target") tr th(scope="row") Value td(data-hook="new-bc-value" data-target="value") tr - th(scope="row") Deterministic + th(scope="row") Concentration td - input(type="checkbox" checked=this.newBC.deterministic data-hook="new-bc-deterministic") + input(type="checkbox" checked=this.newBC.deterministic data-hook="new-bc-deterministic" disabled) div.col-sm-6 From 13077659f1e8fd77e5b2112989f168589e1db7c8 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 10:59:21 -0400 Subject: [PATCH 012/133] Separated the name from the other inputs, fixed error reporting, and added function to toggle the disabled property when the target is selected. --- client/views/boundary-conditions-editor.js | 71 ++++++++++++++++++---- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index d2214e7695..1c710f6b80 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -24,6 +24,7 @@ let Tooltips = require('../tooltips'); //views let InputView = require('./input'); let View = require('ampersand-view'); +let SelectView = require('ampersand-select-view'); let BoundaryConditionView = require('./boundary-condition-view'); //templates let template = require('../templates/includes/boundaryConditionsEditor.pug'); @@ -32,6 +33,8 @@ module.exports = View.extend({ template: template, events: { 'change [data-hook=new-bc-name]' : 'handleSetValue', + 'change [data-hook=new-bc-target]' : 'handleSetTarget', + 'change [data-hook=new-bc-deterministic]' : 'handleSetDeterministic', 'change [data-hook=new-bc-type]' : 'handleSetValue', 'change [data-hook=new-bc-value]' : 'handleSetValue', 'change [data-hook=new-bc-x-min]' : 'handleSetValue', @@ -75,15 +78,35 @@ module.exports = View.extend({ this.setDefaultBC(); this.resetNewBCViews(); }, + handleSetDeterministic: function (e) { + this.newBC.deterministic = e.target.checked; + }, + handleSetTarget: function (e) { + let properties = ["v", "nu", "rho"]; + let target = e.target.value; + if(properties.includes(target)) { + this.newBC.property = target; + this.newBC.species = null; + $(this.queryByHook("new-bc-deterministic")).prop("disabled", true); + }else{ + this.newBC.property = null; + this.newBC.species = target; + $(this.queryByHook("new-bc-deterministic")).prop("disabled", false); + } + }, handleSetValue: function (e) { let key = e.delegateTarget.dataset.target; let value = e.target.value; - if(key.endsWith("min") || key.endsWith("max") || key === "type_id"){ - value = this.validateNewBCCondition(key, value); - }else if(key === "value" && value === "") { - value = null; + if(key === "name") { + this.newBCName = value; + }else{ + if(key.endsWith("min") || key.endsWith("max") || key === "type_id"){ + value = this.validateNewBCCondition(key, value); + }else if(key === "value" && value === "") { + value = null; + } + this.newBC[key] = value; } - this.newBC[key] = value; this.toggleAddNewBCButton(); }, renderEditBoundaryConditionView: function () { @@ -115,7 +138,9 @@ module.exports = View.extend({ ); }, resetNewBCViews: function () { - $(this.queryByHook("new-bc-name")).find("input").val(this.newBC.name); + $(this.queryByHook("new-bc-deterministic")).prop("checked", this.newBC.deterministic); + $(this.queryByHook("new-bc-name")).find("input").val(this.newBCName); + $(this.queryByHook("new-bc-target")).find("select").val(this.newBC.property); $(this.queryByHook("new-bc-type")).find("input").val(this.newBC.type_id); $(this.queryByHook("new-bc-value")).find("input").val(this.newBC.value); $(this.queryByHook("new-bc-x-min")).find("input").val(this.newBC.xmin); @@ -124,15 +149,17 @@ module.exports = View.extend({ $(this.queryByHook("new-bc-y-max")).find("input").val(this.newBC.ymax); $(this.queryByHook("new-bc-z-min")).find("input").val(this.newBC.zmin); $(this.queryByHook("new-bc-z-max")).find("input").val(this.newBC.zmax); + $(this.queryByHook("new-bc-deterministic")).prop("disabled", true); this.toggleAddNewBCButton(); }, setDefaultBC: function () { - this.newBC = {"name": "", "species": null, "property": null, "value": null, "deterministic": true, "type_id": null, + this.newBCName = ""; + this.newBC = {"species": null, "property": "v", "value": null, "deterministic": true, "type_id": null, "xmin": null, "ymin": null, "zmin": null, "xmax": null, "ymax": null, "zmax": null}; this.setConditions = []; }, toggleAddNewBCButton: function () { - let disabled = this.newBC.name === "" || this.newBC.value === null || !this.setConditions.length; + let disabled = this.newBCName === "" || this.newBC.value === null || !this.setConditions.length; $(this.queryByHook("add-new-bc")).prop("disabled", disabled); }, update: function (e) {}, @@ -154,11 +181,29 @@ module.exports = View.extend({ prepareView: function (el) { return new InputView({ parent: this, - required: true, + required: false, name: 'name', - tests: tests.nameTests, valueType: 'string', - value: this.newBC.name, + value: this.newBCName + }); + } + }, + newBCTarget: { + hook: "new-bc-target", + prepareView: function (el) { + let species = this.collection.parent.species.map(function (specie) { + return [specie.compID, specie.name] + }); + let properties = [["v", "Velocity"], ["nu", "Viscosity"], ["rho", "Density"]] + let options = [{groupName: "Properties", options: properties}, + {groupName: "Variables", options: species}] + return new SelectView({ + parent: this, + required: false, + name: 'target', + eagerValidate: true, + groupOptions: options, + value: this.newBC.property }); } }, @@ -167,11 +212,11 @@ module.exports = View.extend({ prepareView: function (el) { return new InputView({ parent: this, - required: true, + required: false, name: 'value', tests: tests.valueTests, valueType: 'number', - value: this.newBC.value, + value: this.newBC.value }); } }, From 12012b5f72abd79ce5eeab33900eaf6d6cef3053 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 11:42:25 -0400 Subject: [PATCH 013/133] Added function to create a new boundary condition using the spatialpy boundary condition constructor. --- stochss/handlers/util/stochss_spatial_model.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index c95183c718..7bde9bd8c9 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -388,6 +388,21 @@ def convert_to_spatialpy(self): return s_model + @classmethod + def create_boundary_condition(cls, kwargs): + ''' + Create a new boundary condition using spatialpy.BoundaryCondition + + Attributes + ---------- + kwargs : dict + Arguments passed to the spatialpy.BoundaryCondition constructor + ''' + new_bc = BoundaryCondition(**kwargs) + expression = new_bc.expression() + return {"expression": expression} + + def get_domain(self, path=None, new=False): ''' Get a prospective domain From 79bdc3a7a8622d74bf7bcc79f1e7f75e8dd647a4 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 11:43:21 -0400 Subject: [PATCH 014/133] Added the api handler and route for the new boundary condition requests. --- stochss/handlers/__init__.py | 1 + stochss/handlers/models.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/stochss/handlers/__init__.py b/stochss/handlers/__init__.py index 426635b43e..f1ae51967e 100644 --- a/stochss/handlers/__init__.py +++ b/stochss/handlers/__init__.py @@ -75,6 +75,7 @@ def get_page_handlers(route_start): (r"/stochss/api/model/to-sbml\/?", ModelToSBMLAPIHandler), (r"/stochss/api/model/run\/?", RunModelAPIHandler), (r"/stochss/api/model/exists\/?", ModelExistsAPIHandler), + (r"/stochss/api/model/new-bc\/?", CreateNewBoundCondAPIHandler), (r"/stochss/api/spatial-model/domain-list\/?", LoadExternalDomains), (r"/stochss/api/spatial-model/types-list\/?", LoadParticleTypesDescriptions), (r"/stochss/api/spatial-model/domain-plot\/?", LoadDomainAPIHandler), diff --git a/stochss/handlers/models.py b/stochss/handlers/models.py index a55a29a947..12e1a38dfd 100644 --- a/stochss/handlers/models.py +++ b/stochss/handlers/models.py @@ -405,3 +405,31 @@ async def get(self): except StochSSAPIError as err: report_error(self, log, err) self.finish() + + +class CreateNewBoundCondAPIHandler(APIHandler): + ''' + ################################################################################################ + Handler creating new boundary conditions. + ################################################################################################ + ''' + @web.authenticated + async def post(self): + ''' + Creates a new restricted boundary condition. + + Attributes + ---------- + ''' + self.set_header('Content-Type', 'application/json') + kwargs = json.loads(self.request.body.decode()) + log.debug("Args passed to the boundary condition constructor: %s", kwargs) + try: + log.info("Creating the new boundary condition") + resp = StochSSSpatialModel.create_boundary_condition(kwargs) + log.info("Successfully created the new boundary condition") + log.debug("Response Message: %s", resp) + self.write(resp) + except StochSSAPIError as err: + report_error(self, log, err) + self.finish() From 295478293921fb0f61690a19bd607b0799ba1c3d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 11:44:50 -0400 Subject: [PATCH 015/133] Added function that will add a new boundary condition to the boundary condition collection. --- client/models/boundary-conditions.js | 9 +++++++++ client/models/model.js | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/client/models/boundary-conditions.js b/client/models/boundary-conditions.js index aeed723ffe..c156d8045f 100644 --- a/client/models/boundary-conditions.js +++ b/client/models/boundary-conditions.js @@ -23,4 +23,13 @@ var BoundaryCondition = require('./boundary-condition'); module.exports = Collection.extend({ model: BoundaryCondition, + addNewBoundaryCondition: function (name, expression) { + let id = this.parent.getDefaultID(); + let boundaryCondition = this.add({ + compID: id, + name: name, + expression: expression, + annotation: "" + }); + } }); \ No newline at end of file diff --git a/client/models/model.js b/client/models/model.js index d5ca4c628c..1f98d11757 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -30,7 +30,7 @@ var Reactions = require('./reactions'); var Rules = require('./rules'); var Events = require('./events'); var FunctionDefinitions = require('./function-definitions'); -var BoundaryCondition = require('./boundary-conditions'); +var BoundaryConditions = require('./boundary-conditions'); module.exports = Model.extend({ url: function () { @@ -51,7 +51,7 @@ module.exports = Model.extend({ rules: Rules, eventsCollection: Events, functionDefinitions: FunctionDefinitions, - boundaryConditions: BoundaryCondition + boundaryConditions: BoundaryConditions }, children: { modelSettings: TimespanSettings, From 709a463f1c339efd696a5580a77a63ae7b9fadcd Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 11:45:59 -0400 Subject: [PATCH 016/133] Added the post xhr request that create and return the expression. --- client/views/boundary-conditions-editor.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index 1c710f6b80..5bd4b9cff0 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ let $ = require('jquery'); +let path = require('path'); //support files let app = require('../app'); let tests = require('./tests'); @@ -74,9 +75,15 @@ module.exports = View.extend({ app.changeCollapseButtonText(this, e); }, handleAddBCClick: function (e) { - let newBC = JSON.stringify(this.newBC) - this.setDefaultBC(); - this.resetNewBCViews(); + let endpoint = path.join(app.getApiPath(), "model/new-bc") + let self = this; + app.postXHR(endpoint, this.newBC, { + success: function (err, response, body) { + self.collection.addNewBoundaryCondition(self.newBCName, body.expression); + self.setDefaultBC(); + self.resetNewBCViews(); + } + }); }, handleSetDeterministic: function (e) { this.newBC.deterministic = e.target.checked; From a76f059d2d407f1651915620c0015a3936c57c5e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 9 Jun 2021 14:53:29 -0400 Subject: [PATCH 017/133] Added boundary conditions to the model template. --- stochss_templates/nonSpatialModelTemplate.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stochss_templates/nonSpatialModelTemplate.json b/stochss_templates/nonSpatialModelTemplate.json index 248896e2cc..5f95a1c9c5 100644 --- a/stochss_templates/nonSpatialModelTemplate.json +++ b/stochss_templates/nonSpatialModelTemplate.json @@ -40,5 +40,6 @@ "reactions": [], "rules": [], "eventsCollection": [], - "functionDefinitions": [] + "functionDefinitions": [], + "boundaryConditions": [] } From 3080f131919f93b7c21291e350b4a3ae708809f2 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 14:11:24 -0400 Subject: [PATCH 018/133] Refactored the view mode to use tabs instead of a save button. Implemented a readOnly property to allow the model editor and model viewer to use the same view file. --- client/pages/model-editor.js | 9 +- client/views/model-viewer.js | 7 +- client/views/reaction-listing.js | 19 +++- client/views/reactions-editor.js | 144 +++++++++++++++++++------------ 4 files changed, 111 insertions(+), 68 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index ce70b0177e..8b6c29c51a 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -35,7 +35,6 @@ var ParametersEditorView = require('../views/parameters-editor'); var ParameterViewer = require('../views/parameters-viewer'); var ParticleViewer = require('../views/view-particle'); var ReactionsEditorView = require('../views/reactions-editor'); -var ReactionsViewer = require('../views/reactions-viewer'); var EventsEditorView = require('../views/events-editor'); var EventsViewer = require('../views/events-viewer'); var RulesEditorView = require('../views/rules-editor'); @@ -326,15 +325,11 @@ let ModelEditor = PageView.extend({ } app.registerRenderSubview(this, this.parametersEditor, 'parameters-editor-container'); }, - renderReactionsView: function (mode="edit", opened=false) { + renderReactionsView: function () { if(this.reactionsEditor) { this.reactionsEditor.remove() } - if(mode === "edit") { - this.reactionsEditor = new ReactionsEditorView({collection: this.model.reactions, opened: opened}); - }else{ - this.reactionsEditor = new ReactionsViewer({collection: this.model.reactions}); - } + this.reactionsEditor = new ReactionsEditorView({collection: this.model.reactions}); app.registerRenderSubview(this, this.reactionsEditor, 'reactions-editor-container'); }, renderEventsView: function (mode="edit", opened=false) { diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index d35fe7885a..5ed2c53954 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -23,7 +23,7 @@ let View = require('ampersand-view'); let RulesViewer = require('./rules-viewer'); let EventsViewer = require('./events-viewer'); let SpeciesViewer = require('./species-viewer'); -let ReactionsViewer = require('./reactions-viewer'); +let ReactionsEditor = require('./reactions-editor'); let ParametersViewer = require('./parameters-viewer'); let SBMLComponentsView = require('./sbml-component-editor'); //templates @@ -59,8 +59,9 @@ module.exports = View.extend({ app.registerRenderSubview(this, parametersViewer, "parameters-viewer-container"); }, renderReactionsView: function () { - let reactionsViewer = new ReactionsViewer({ - collection: this.model.reactions + let reactionsViewer = new ReactionsEditor({ + collection: this.model.reactions, + readOnly: true }); app.registerRenderSubview(this, reactionsViewer, "reactions-viewer-container"); }, diff --git a/client/views/reaction-listing.js b/client/views/reaction-listing.js index 864c24a43d..9e1d4eb315 100644 --- a/client/views/reaction-listing.js +++ b/client/views/reaction-listing.js @@ -25,10 +25,10 @@ var modals = require('../modals'); var View = require('ampersand-view'); var InputView = require('./input'); //templates -var template = require('../templates/includes/reactionListing.pug'); +var editTemplate = require('../templates/includes/reactionListing.pug'); +let viewTemplate = require('../templates/includes/viewReactions.pug'); module.exports = View.extend({ - template: template, bindings: { 'model.name' : { type: 'value', @@ -58,8 +58,21 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode) { + this.rate = this.model.reactionType === "custom-propensity" ? this.model.propensity : this.model.rate.name; + this.types = []; + let self = this; + if(this.model.types) { + this.model.types.forEach(function (index) { + let type = self.model.collection.parent.domain.types.get(index, "typeID"); + self.types.push(type.name) + }); + } + } }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); @@ -100,7 +113,7 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderReactionListingView(); + self.parent.renderReactionListingViews(); modal.modal('hide'); }); }, diff --git a/client/views/reactions-editor.js b/client/views/reactions-editor.js index c54ef00484..96f30382a7 100644 --- a/client/views/reactions-editor.js +++ b/client/views/reactions-editor.js @@ -45,69 +45,83 @@ module.exports = View.extend({ 'click [data-hook=four]' : 'handleAddReactionClick', 'click [data-hook=custom-massaction]' : 'handleAddReactionClick', 'click [data-hook=custom-propensity]' : 'handleAddReactionClick', - 'click [data-hook=save-reactions]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.tooltips = Tooltips.reactionsEditor - this.opened = attrs.opened - this.collection.on("select", function (reaction) { - this.setSelectedReaction(reaction); - this.setDetailsView(reaction); - }, this); - this.collection.on("remove", function (reaction) { - // Select the last reaction by default - // But only if there are other reactions other than the one we're removing - if (reaction.detailsView) - reaction.detailsView.remove(); - this.collection.removeReaction(reaction); - if (this.collection.length) { - var selected = this.collection.at(this.collection.length-1); - this.collection.trigger("select", selected); - } - }, this); - this.collection.parent.species.on('add remove', this.toggleAddReactionButton, this); - this.collection.parent.parameters.on('add remove', this.toggleReactionTypes, this); - this.collection.parent.on('change', this.toggleProcessError, this) + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + if(!this.readOnly) { + this.collection.on("select", function (reaction) { + this.setSelectedReaction(reaction); + this.setDetailsView(reaction); + }, this); + this.collection.on("remove", function (reaction) { + // Select the last reaction by default + // But only if there are other reactions other than the one we're removing + if (reaction.detailsView) + reaction.detailsView.remove(); + this.collection.removeReaction(reaction); + if (this.collection.length) { + var selected = this.collection.at(this.collection.length-1); + this.collection.trigger("select", selected); + } + }, this); + this.collection.parent.species.on('add remove', this.toggleAddReactionButton, this); + this.collection.parent.parameters.on('add remove', this.toggleReactionTypes, this); + this.collection.parent.on('change', this.toggleProcessError, this) + } }, render: function () { View.prototype.render.apply(this, arguments); - this.renderReactionListingView(); - this.detailsContainer = this.queryByHook('reaction-details-container'); - this.detailsViewSwitcher = new ViewSwitcher({ - el: this.detailsContainer, - }); - if (this.collection.length) { - this.setSelectedReaction(this.collection.at(0)); - this.collection.trigger("select", this.selectedReaction); - } - this.collection.trigger("change"); - this.toggleAddReactionButton(); - if(this.collection.parent.parameters.length > 0){ - $(this.queryByHook('add-reaction-partial')).prop('hidden', true); - } - else{ - $(this.queryByHook('add-reaction-full')).prop('hidden', true); - } - this.renderReactionTypes(); - katex.render("\\emptyset", this.queryByHook('emptyset'), { - displayMode: false, - output: 'html', - }); - if(this.opened) { - this.openReactionsContainer(); + if(this.readOnly) { + this.renderViewReactionView() + $(this.queryByHook('reactions-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('reactions-view-tab')).tab('show'); + $(this.queryByHook('edit-reactions')).removeClass('active'); + $(this.queryByHook('view-reactions')).addClass('active'); + }else{ + this.renderReactionListingViews(); + this.detailsContainer = this.queryByHook('reaction-details-container'); + this.detailsViewSwitcher = new ViewSwitcher({ + el: this.detailsContainer, + }); + if (this.collection.length) { + this.setSelectedReaction(this.collection.at(0)); + this.collection.trigger("select", this.selectedReaction); + } + this.collection.trigger("change"); + this.toggleAddReactionButton(); + if(this.collection.parent.parameters.length > 0){ + $(this.queryByHook('add-reaction-partial')).prop('hidden', true); + } + else{ + $(this.queryByHook('add-reaction-full')).prop('hidden', true); + } + this.renderReactionTypes(); + katex.render("\\emptyset", this.queryByHook('emptyset'), { + displayMode: false, + output: 'html', + }); + this.toggleProcessError() + $(this.queryByHook('massaction-message')).prop('hidden', this.collection.parent.parameters.length > 0); } - this.toggleProcessError() - $(this.queryByHook('massaction-message')).prop('hidden', this.collection.parent.parameters.length > 0); }, update: function () { }, updateValid: function () { }, - renderReactionListingView: function () { - if(this.reactionListingView){ - this.reactionListingView.remove(); + renderReactionListingViews: function () { + this.renderEditReactionListingView(); + this.renderViewReactionView(); + }, + renderEditReactionListingView: function () { + if(this.editReactionListingView){ + this.editReactionListingView.remove(); } if(this.collection.parent.parameters.length <= 0) { for(var i = 0; i < this.collection.length; i++) { @@ -116,10 +130,10 @@ module.exports = View.extend({ } } } - this.reactionListingView = this.renderCollection( + this.editReactionListingView = this.renderCollection( this.collection, ReactionListingView, - this.queryByHook('reaction-list') + this.queryByHook('edit-reaction-list') ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); @@ -128,6 +142,27 @@ module.exports = View.extend({ }); }); }, + renderViewReactionView: function () { + if(this.viewReactionListingView){ + this.viewReactionListingView.remove(); + } + if(!this.collection.parent.is_spatial){ + $(this.queryByHook("reaction-types-header")).css("display", "none"); + } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("reaction-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("reaction-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {viewMode: true, hasAnnotations: this.containsMdlWithAnn}} + this.viewReactionListingView = this.renderCollection( + this.collection, + ReactionListingView, + this.queryByHook('view-reaction-list'), + options + ); + }, toggleAddReactionButton: function () { $(this.queryByHook('add-reaction-full')).prop('disabled', (this.collection.parent.species.length <= 0)); $(this.queryByHook('add-reaction-partial')).prop('disabled', (this.collection.parent.species.length <= 0)); @@ -186,10 +221,6 @@ module.exports = View.extend({ detailsView.parent = this; return detailsView }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderReactionsView(mode="view"); - }, openReactionsContainer: function () { $(this.queryByHook('reactions-list-container')).collapse('show'); let collapseBtn = $(this.queryByHook('collapse')) @@ -207,6 +238,9 @@ module.exports = View.extend({ }, toggleProcessError: function () { let errorMsg = $(this.queryByHook('process-component-error')) + if(this.collection.parent.is_spatial){ + errorMsg.html("

A model must have at least one reaction

") + } let model = this.collection.parent if(this.collection.length <= 0 && model.eventsCollection.length <= 0 && model.rules.length <= 0) { errorMsg.addClass('component-invalid') From 28ed7d388373316312d0092f8e49e8d85575e339 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 14:14:07 -0400 Subject: [PATCH 019/133] Refactored the reactions editor layout to use the row/col class system instead of the table system. --- client/templates/includes/reactionListing.pug | 32 ++-- client/templates/includes/reactionsEditor.pug | 160 +++++++++++------- client/templates/includes/viewReactions.pug | 29 ++-- 3 files changed, 135 insertions(+), 86 deletions(-) diff --git a/client/templates/includes/reactionListing.pug b/client/templates/includes/reactionListing.pug index bed0ba53e5..3b7ea5cae3 100644 --- a/client/templates/includes/reactionListing.pug +++ b/client/templates/includes/reactionListing.pug @@ -1,14 +1,24 @@ -tr - td: input(type="radio", data-hook="select", name="reaction-select") - - td.name: div(data-hook="input-name-container") - - td: div(data-hook="summary", style="width: auto !important") - - td +div.mx-1 - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + if(this.model.collection.indexOf(this.model) !== 0) + hr - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + div.row.align-items-baseline - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file + div.col-md-2 + + div.pl-3: input(type="radio" data-hook="select" name="reaction-select") + + div.col-md-2(data-hook="input-name-container") + + div.col-md-3.reaction-list-summary(data-hook="summary") + + div.col-md-3 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-md-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/reactionsEditor.pug b/client/templates/includes/reactionsEditor.pug index b27791913b..c681921f27 100644 --- a/client/templates/includes/reactionsEditor.pug +++ b/client/templates/includes/reactionsEditor.pug @@ -1,78 +1,108 @@ -div#reactions-editor.card.card-body +div#reactions-editor.card - div + div.card-header.pb-0 - h3.inline - div - div.inline Reactions + h3.inline.mr-3 Reactions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-reaction", data-hook="collapse") + + div.inline.mr-3 - div.collapse(id="collapse-reaction" data-hook="reactions-list-container") + ul.nav.nav-tabs.card-header-tabs(id="reactions-tabs") - p - | A process that transforms a set of Variables (reactants) into a set of Variables (products) at a rate set by a Parameter. Select from the given reaction templates, or use the custom types. The symbol represents null or empty set. + li.nav-item - div.row - div.col-md-7.container-part: table.table - thead: tr - th(scope="col") Edit - th(scope="col") - div - div.inline Name + a.nav-link.tab.active(data-hook="reactions-edit-tab" data-toggle="tab" href="#edit-reactions") Edit - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - - th(scope="col") Summary - th(scope="col") - div - div.inline Annotation + li.nav-item + + a.nav-link.tab(data-hook="reactions-view-tab" data-toggle="tab" href="#view-reactions") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-reaction" data-hook="collapse") + + + div.card-body.collapse.tab-content(id="collapse-reaction" data-hook="reactions-list-container") + + div.tab-pane.active(id="edit-reactions" data-hook="edit-reactions") + + p + | A process that transforms a set of Variables (reactants) into a set of Variables (products) at a rate set by a Parameter. Select from the given reaction templates, or use the custom types. The symbol represents null or empty set. + + div.row.mb-3 + div.col-md-7.container-part + div.mx-1.row.head.align-items-baseline + div.col-md-2: h6.align-bottom Edit + + div.col-md-2 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-md-3: h6 Summary + + div.col-md-3 + + div.inline + + h6 Annotation div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-md-2: h6 Remove - th(scope="col") Remove + div.mt-3(data-hook="edit-reaction-list") - tbody(data-hook="reaction-list") + div.col-md-5.container-part(data-hook="reaction-details-container") - div.col-md-5.container-part(data-hook="reaction-details-container") + div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter + + div(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule - div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter - - div(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule - - div.dropdown.inline - - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( - data-hook="add-reaction-full", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Reaction - - ul.dropdown-menu(aria-labelledby="addReactionBtn") - li.dropdown-item(data-hook="creation") Creation Reaction - li.dropdown-item(data-hook="destruction") Destruction Reaction - li.dropdown-item(data-hook="change") Transformation Reaction - li.dropdown-item(data-hook="dimerization") Dimerization Reaction - li.dropdown-item(data-hook="merge") Merge Reaction - li.dropdown-item(data-hook="split") Split Reaction - li.dropdown-item(data-hook="four") Four Reaction - li.dropdown-divider - li.dropdown-item(data-hook="custom-massaction") Custom mass action - li.dropdown-item(data-hook="custom-propensity") Custom propensity - - div.dropdown.inline - - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( - data-hook="add-reaction-partial", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Reaction - - ul.dropdown-menu(aria-labelledby="addReactionBtn") - li.dropdown-item(data-hook="custom-propensity") Custom propensity - - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-reactions") Save Reactions + div.dropdown.inline + + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( + data-hook="add-reaction-full", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Reaction + + ul.dropdown-menu(aria-labelledby="addReactionBtn") + li.dropdown-item(data-hook="creation") Creation Reaction + li.dropdown-item(data-hook="destruction") Destruction Reaction + li.dropdown-item(data-hook="change") Transformation Reaction + li.dropdown-item(data-hook="dimerization") Dimerization Reaction + li.dropdown-item(data-hook="merge") Merge Reaction + li.dropdown-item(data-hook="split") Split Reaction + li.dropdown-item(data-hook="four") Four Reaction + li.dropdown-divider + li.dropdown-item(data-hook="custom-massaction") Custom mass action + li.dropdown-item(data-hook="custom-propensity") Custom propensity + + div.dropdown.inline + + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( + data-hook="add-reaction-partial", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Reaction + + ul.dropdown-menu(aria-labelledby="addReactionBtn") + li.dropdown-item(data-hook="custom-propensity") Custom propensity + + div.tab-pane(id="view-reactions" data-hook="view-reactions") + + div.mx-1.row.head + + div.col-md-2: h6 Name + + div.col-md-3: h6 Summary + + div.col-md-3: h6 Rate/Propensity + + div.col-md-2(data-hook="reaction-types-header"): h6 Active in Types + + div.col-md-2(data-hook="reaction-annotation-header"): h6 Annotation + + div.mt-3(data-hook="view-reaction-list") diff --git a/client/templates/includes/viewReactions.pug b/client/templates/includes/viewReactions.pug index 582e48dae1..21be6b3275 100644 --- a/client/templates/includes/viewReactions.pug +++ b/client/templates/includes/viewReactions.pug @@ -1,15 +1,24 @@ -tr - td=this.model.name +div.mx-1 - td: div(data-hook="summary") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.rate + div.row.align-items-baseline - if this.model.collection.parent.is_spatial - td: div=this.types.join(', ') + div.col-md-2 - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.pl-2=this.model.name - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + div.col-md-3(data-hook="summary") + + div.col-md-3=this.rate + + if this.model.collection.parent.is_spatial + div.col-md-2(data-hook="reaction-types") + + div=this.types.join(', ') + + div.col-md-2(data-hook="reaction-annotation-header") + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation) From 435a0059c4ec5ff16f970bdcd6573de43da912bf Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 14:16:12 -0400 Subject: [PATCH 020/133] Added styling to center the reaction summaries in the reaction listings section. --- client/styles/styles.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/styles/styles.css b/client/styles/styles.css index 29403e2122..e5b21c765d 100644 --- a/client/styles/styles.css +++ b/client/styles/styles.css @@ -293,6 +293,11 @@ input[type="file"]::-ms-browse { display: inline-block; } +.reaction-list-summary { + width: auto !important; + text-align: center; +} + .container-part { max-width: none; } From 87f5af2e9b11eac557a3ba6e6b69380999b7c783 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 14:16:32 -0400 Subject: [PATCH 021/133] Removed dead files. --- client/templates/includes/reactionsViewer.pug | 24 --------- client/views/reactions-viewer.js | 54 ------------------- client/views/view-reactions.js | 48 ----------------- 3 files changed, 126 deletions(-) delete mode 100644 client/templates/includes/reactionsViewer.pug delete mode 100644 client/views/reactions-viewer.js delete mode 100644 client/views/view-reactions.js diff --git a/client/templates/includes/reactionsViewer.pug b/client/templates/includes/reactionsViewer.pug deleted file mode 100644 index 2ad0a5bb15..0000000000 --- a/client/templates/includes/reactionsViewer.pug +++ /dev/null @@ -1,24 +0,0 @@ -div#reactions-viewer.card.card-body - - div - - h3.inline Reactions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-reactions", data-hook="collapse") - - - div.collapse(class="show", id="collapse-reactions") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - th.col-md-3-view(scope="col") Summary - th.col-md-6-view(scope="col") Rate/Propensity - if this.collection.parent.is_spatial - th.col-md-3-view(scope="col") Active in Types - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="reaction-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-reactions") Edit Reactions diff --git a/client/views/reactions-viewer.js b/client/views/reactions-viewer.js deleted file mode 100644 index ee31d9b390..0000000000 --- a/client/views/reactions-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewReactions = require('./view-reactions'); -//templates -var template = require('../templates/includes/reactionsViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-reactions]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewReactions, this.queryByHook('reaction-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderReactionsView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/view-reactions.js b/client/views/view-reactions.js deleted file mode 100644 index be5a565ab5..0000000000 --- a/client/views/view-reactions.js +++ /dev/null @@ -1,48 +0,0 @@ -/* -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 . -*/ - -var katex = require('katex'); -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewReactions.pug'); - -module.exports = View.extend({ - template: template, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - let self = this; - this.types = []; - if(this.model.types) { - this.model.types.forEach(function (index) { - let type = self.model.collection.parent.domain.types.get(index, "typeID"); - self.types.push(type.name) - }); - } - this.rate = this.model.reactionType === "custom-propensity" ? - this.model.propensity : this.model.rate.name - }, - render: function () { - View.prototype.render.apply(this, arguments); - katex.render(this.model.summary, this.queryByHook('summary'), { - displayMode: false, - output: 'html', - throwOnError: false - }); - }, -}); \ No newline at end of file From 6a185e1c36fbf498367a8e340dbf1f8a5f30d01f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 15:27:52 -0400 Subject: [PATCH 022/133] Fixed issue with displaying annotations section when boundary conditions don't have annotations. --- client/templates/includes/boundaryConditionsEditor.pug | 6 ++---- client/templates/includes/viewBoundaryCondition.pug | 4 +++- client/views/boundary-conditions-editor.js | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug index 8cfddaea37..0e6f1fbfdb 100644 --- a/client/templates/includes/boundaryConditionsEditor.pug +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -115,10 +115,8 @@ div#boundary-conditions.card div.col-sm-7: h6 Expression - div.col-sm-2 - - div.inline Annotation + div.col-sm-2(data-hook="bc-annotation-header") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + h6 Annotation div.mt-3(data-hook="view-boundary-conditions-list") diff --git a/client/templates/includes/viewBoundaryCondition.pug b/client/templates/includes/viewBoundaryCondition.pug index a78f94d10b..6ea4db586d 100644 --- a/client/templates/includes/viewBoundaryCondition.pug +++ b/client/templates/includes/viewBoundaryCondition.pug @@ -15,4 +15,6 @@ div.mx-1 div.col-md-2 - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + if this.model.annotation + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index 5bd4b9cff0..1255953dc2 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -136,6 +136,12 @@ module.exports = View.extend({ if(this.viewBoundaryConditionView) { this.viewBoundaryConditionView.remove(); } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("bc-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("bc-annotation-header")).css("display", "block"); + } let options = {viewOptions: {viewMode: true}}; this.viewBoundaryConditionView = this.renderCollection( this.collection, From 6e6aa854b7d098c720024483dad23f4a782048fb Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 17:23:19 -0400 Subject: [PATCH 023/133] Moved info block outside of the collapse div. Added missing hr tag in view pane. --- .../includes/boundaryConditionsEditor.pug | 137 +++++++++--------- 1 file changed, 71 insertions(+), 66 deletions(-) diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug index 0e6f1fbfdb..61dc60bbc7 100644 --- a/client/templates/includes/boundaryConditionsEditor.pug +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -18,105 +18,110 @@ div#boundary-conditions.card button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-boundary-conditions", data-hook="collapse-bc") + - div.card-body.collapse.tab-content(id="collapse-boundary-conditions") + div.card-body - div.tab-pane.active(id="edit-boundary-conditions" data-hook="edit-boundary-conditions") + p.mb-0 + | Set spatial regions of the domain where a property of particles are held constant. - p Set spatial regions of the domain where a property of particles are held constant. + div.collapse.tab-content(id="collapse-boundary-conditions") - hr + div.tab-pane.active(id="edit-boundary-conditions" data-hook="edit-boundary-conditions") - div.mx-1.row.head + hr - div.col-sm-3: h6 Name + div.mx-1.row.head - div.col-sm-5: h6 Expression + div.col-sm-3: h6 Name - div.col-sm-2 + div.col-sm-5: h6 Expression - div.inline Annotation + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.inline Annotation - div.col-sm-2: h6 Remove + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - div.mt-3(data-hook="edit-boundary-conditions-list") + div.col-sm-2: h6 Remove - div.card.mt-3 + div.mt-3(data-hook="edit-boundary-conditions-list") - div.card-header + div.card.mt-3 - h4.inline New Boundary Condition + div.card-header - button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-new-boundary-conditions" data-hook="collapse-new-bc") + + h4.inline New Boundary Condition - div.card-body.mx-1.collapse(id="collapse-new-boundary-conditions") + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-new-boundary-conditions" data-hook="collapse-new-bc") + - div.row.head + div.card-body.mx-1.collapse(id="collapse-new-boundary-conditions") - div.col-sm-2: h6 Name + div.row.head - div.col-sm-4: h6 Target + div.col-sm-2: h6 Name - div.col-sm-6: h6 Conditions + div.col-sm-4: h6 Target - div.row.mt-3 + div.col-sm-6: h6 Conditions - div.col-sm-2(data-hook="new-bc-name" data-target="name") + div.row.mt-3 - div.col-sm-4 + div.col-sm-2(data-hook="new-bc-name" data-target="name") - table.table - tbody - tr - th(scope="row") Property/Variable - td(data-hook="new-bc-target") - tr - th(scope="row") Value - td(data-hook="new-bc-value" data-target="value") - tr - th(scope="row") Concentration - td - input(type="checkbox" checked=this.newBC.deterministic data-hook="new-bc-deterministic" disabled) + div.col-sm-4 - div.col-sm-6 + table.table + tbody + tr + th(scope="row") Property/Variable + td(data-hook="new-bc-target") + tr + th(scope="row") Value + td(data-hook="new-bc-value" data-target="value") + tr + th(scope="row") Concentration + td + input(type="checkbox" checked=this.newBC.deterministic data-hook="new-bc-deterministic" disabled) - div + div.col-sm-6 - span.inline.mr-3(for="new-bc-type") Type: - div.inline(id="new-bc-type" data-hook="new-bc-type" data-target="type_id") + div - table.table - thead - tr - th(scope="col") - th(scope="col") X-Axis - th(scope="col") Y-Axis - th(scope="col") Z-Axis - tbody - tr - th(scope="row") Min - td: div(data-hook="new-bc-x-min" data-target="xmin") - td: div(data-hook="new-bc-y-min" data-target="ymin") - td: div(data-hook="new-bc-z-min" data-target="zmin") - tr - th(scope="row") Max - td: div(data-hook="new-bc-x-max" data-target="xmax") - td: div(data-hook="new-bc-y-max" data-target="ymax") - td: div(data-hook="new-bc-z-max" data-target="zmax") + span.inline.mr-3(for="new-bc-type") Type: + div.inline(id="new-bc-type" data-hook="new-bc-type" data-target="type_id") - button.btn.btn-outline-primary.box-shadow(data-hook="add-new-bc") Add New Boundary Condition + table.table + thead + tr + th(scope="col") + th(scope="col") X-Axis + th(scope="col") Y-Axis + th(scope="col") Z-Axis + tbody + tr + th(scope="row") Min + td: div(data-hook="new-bc-x-min" data-target="xmin") + td: div(data-hook="new-bc-y-min" data-target="ymin") + td: div(data-hook="new-bc-z-min" data-target="zmin") + tr + th(scope="row") Max + td: div(data-hook="new-bc-x-max" data-target="xmax") + td: div(data-hook="new-bc-y-max" data-target="ymax") + td: div(data-hook="new-bc-z-max" data-target="zmax") - div.tab-pane(id="view-boundary-conditions" data-hook="view-boundary-conditions") + button.btn.btn-outline-primary.box-shadow(data-hook="add-new-bc") Add New Boundary Condition - div.mx-1.row.head + div.tab-pane(id="view-boundary-conditions" data-hook="view-boundary-conditions") - div.col-sm-3: h6 Name + hr - div.col-sm-7: h6 Expression + div.mx-1.row.head - div.col-sm-2(data-hook="bc-annotation-header") + div.col-sm-3: h6 Name - h6 Annotation + div.col-sm-7: h6 Expression - div.mt-3(data-hook="view-boundary-conditions-list") + div.col-sm-2(data-hook="bc-annotation-header") + + h6 Annotation + + div.mt-3(data-hook="view-boundary-conditions-list") From b6a8d99f7ec700e28f1989b5c3b48aad92bcd3e6 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 17:24:01 -0400 Subject: [PATCH 024/133] Fixed viewer update issue. --- client/views/boundary-condition-view.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/views/boundary-condition-view.js b/client/views/boundary-condition-view.js index 6c88802b95..a5d5a0e05a 100644 --- a/client/views/boundary-condition-view.js +++ b/client/views/boundary-condition-view.js @@ -66,7 +66,8 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderBoundaryConditionView(); + self.parent.renderEditBoundaryConditionView(); + self.parent.renderViewBoundaryConditionView(); modal.modal('hide'); }); }, From e5a851fedca6bf659de4883fdc755698572e4ebd Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 17:43:42 -0400 Subject: [PATCH 025/133] Fixed viewer update issue. --- client/views/reaction-details.js | 1 + client/views/reaction-listing.js | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/views/reaction-details.js b/client/views/reaction-details.js index 36e1949844..aa074e024b 100644 --- a/client/views/reaction-details.js +++ b/client/views/reaction-details.js @@ -182,6 +182,7 @@ module.exports = View.extend({ var val = e.target.selectedOptions.item(0).text; var param = this.getRateFromParameters(val); this.model.rate = param || this.model.rate; + this.model.trigger("change"); this.model.collection.trigger("change"); } }, diff --git a/client/views/reaction-listing.js b/client/views/reaction-listing.js index 9e1d4eb315..6de8a56d3e 100644 --- a/client/views/reaction-listing.js +++ b/client/views/reaction-listing.js @@ -18,6 +18,7 @@ along with this program. If not, see . var $ = require('jquery'); var katex = require('katex'); +let _ = require('underscore'); //support files var tests = require('./tests'); var modals = require('../modals'); @@ -69,6 +70,8 @@ module.exports = View.extend({ self.types.push(type.name) }); } + }else{ + this.model.on('change', _.bind(this.updateViewer, this)); } }, render: function () { @@ -113,10 +116,13 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderReactionListingViews(); + self.parent.renderEditReactionListingView(); modal.modal('hide'); }); }, + updateViewer: function () { + this.parent.renderViewReactionView(); + }, subviews: { inputName: { hook: 'input-name-container', From c9c291b27a9a683e42d61b9e345d9be51211395c Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 10 Jun 2021 17:44:37 -0400 Subject: [PATCH 026/133] Moved the info text outside of the collapse div. Added missing hr tags for view and edit tabs. --- client/templates/includes/reactionsEditor.pug | 133 ++++++++++-------- 1 file changed, 71 insertions(+), 62 deletions(-) diff --git a/client/templates/includes/reactionsEditor.pug b/client/templates/includes/reactionsEditor.pug index c681921f27..a05d6ee8b5 100644 --- a/client/templates/includes/reactionsEditor.pug +++ b/client/templates/includes/reactionsEditor.pug @@ -18,91 +18,100 @@ div#reactions-editor.card button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-reaction" data-hook="collapse") + - div.card-body.collapse.tab-content(id="collapse-reaction" data-hook="reactions-list-container") + div.card-body - div.tab-pane.active(id="edit-reactions" data-hook="edit-reactions") + p.mb-0 + | A process that transforms a set of Variables (reactants) into a set of Variables (products) at a rate set by a Parameter. Select from the given reaction templates, or use the custom types. The symbol represents null or empty set. - p - | A process that transforms a set of Variables (reactants) into a set of Variables (products) at a rate set by a Parameter. Select from the given reaction templates, or use the custom types. The symbol represents null or empty set. + div.collapse.tab-content(id="collapse-reaction" data-hook="reactions-list-container") - div.row.mb-3 - div.col-md-7.container-part - div.mx-1.row.head.align-items-baseline - div.col-md-2: h6.align-bottom Edit - - div.col-md-2 + div.tab-pane.active(id="edit-reactions" data-hook="edit-reactions") - h6.inline Name + div.row.mb-3 + + div.col-md-7.container-part + + hr - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + div.mx-1.row.head.align-items-baseline + + div.col-md-2: h6.align-bottom Edit + + div.col-md-2 - div.col-md-3: h6 Summary + h6.inline Name - div.col-md-3 + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - div.inline + div.col-md-3: h6 Summary - h6 Annotation + div.col-md-3 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - - div.col-md-2: h6 Remove + div.inline - div.mt-3(data-hook="edit-reaction-list") + h6 Annotation - div.col-md-5.container-part(data-hook="reaction-details-container") + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-md-2: h6 Remove - div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter - - div(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule + div.mt-3(data-hook="edit-reaction-list") - div.dropdown.inline + div.col-md-5.container-part(data-hook="reaction-details-container") - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( - data-hook="add-reaction-full", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Reaction + div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter + + div(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule - ul.dropdown-menu(aria-labelledby="addReactionBtn") - li.dropdown-item(data-hook="creation") Creation Reaction - li.dropdown-item(data-hook="destruction") Destruction Reaction - li.dropdown-item(data-hook="change") Transformation Reaction - li.dropdown-item(data-hook="dimerization") Dimerization Reaction - li.dropdown-item(data-hook="merge") Merge Reaction - li.dropdown-item(data-hook="split") Split Reaction - li.dropdown-item(data-hook="four") Four Reaction - li.dropdown-divider - li.dropdown-item(data-hook="custom-massaction") Custom mass action - li.dropdown-item(data-hook="custom-propensity") Custom propensity + div.dropdown.inline - div.dropdown.inline + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( + data-hook="add-reaction-full", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Reaction - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( - data-hook="add-reaction-partial", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Reaction + ul.dropdown-menu(aria-labelledby="addReactionBtn") + li.dropdown-item(data-hook="creation") Creation Reaction + li.dropdown-item(data-hook="destruction") Destruction Reaction + li.dropdown-item(data-hook="change") Transformation Reaction + li.dropdown-item(data-hook="dimerization") Dimerization Reaction + li.dropdown-item(data-hook="merge") Merge Reaction + li.dropdown-item(data-hook="split") Split Reaction + li.dropdown-item(data-hook="four") Four Reaction + li.dropdown-divider + li.dropdown-item(data-hook="custom-massaction") Custom mass action + li.dropdown-item(data-hook="custom-propensity") Custom propensity - ul.dropdown-menu(aria-labelledby="addReactionBtn") - li.dropdown-item(data-hook="custom-propensity") Custom propensity + div.dropdown.inline - div.tab-pane(id="view-reactions" data-hook="view-reactions") + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addReactionBtn( + data-hook="add-reaction-partial", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Reaction - div.mx-1.row.head + ul.dropdown-menu(aria-labelledby="addReactionBtn") + li.dropdown-item(data-hook="custom-propensity") Custom propensity - div.col-md-2: h6 Name + div.tab-pane(id="view-reactions" data-hook="view-reactions") - div.col-md-3: h6 Summary + hr - div.col-md-3: h6 Rate/Propensity + div.mx-1.row.head - div.col-md-2(data-hook="reaction-types-header"): h6 Active in Types - - div.col-md-2(data-hook="reaction-annotation-header"): h6 Annotation + div.col-md-2: h6 Name - div.mt-3(data-hook="view-reaction-list") + div.col-md-3: h6 Summary + + div.col-md-3: h6 Rate/Propensity + + div.col-md-2(data-hook="reaction-types-header"): h6 Active in Types + + div.col-md-2(data-hook="reaction-annotation-header"): h6 Annotation + + div.mt-3(data-hook="view-reaction-list") From d8fda6109e4394072bb699774aba0cd3a64f1983 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Sun, 13 Jun 2021 16:22:32 -0400 Subject: [PATCH 027/133] Removed the species viewer. --- client/pages/model-editor.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index bdb85123e5..718f43c3f2 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -28,7 +28,6 @@ var PageView = require('../pages/base'); var InputView = require('../views/input'); var DomainViewer = require('../views/domain-viewer'); var SpeciesEditorView = require('../views/species-editor'); -var SpeciesViewer = require('../views/species-viewer'); var InitialConditionsEditorView = require('../views/initial-conditions-editor'); var InitialConditionsViewer = require('../views/initial-conditions-viewer'); var ParametersEditorView = require('../views/parameters-editor'); @@ -287,15 +286,14 @@ let ModelEditor = PageView.extend({ app.registerRenderSubview(this, this.domainViewer, 'domain-viewer-container'); } }, - renderSpeciesView: function (mode="edit") { + renderSpeciesView: function () { if(this.speciesEditor) { this.speciesEditor.remove() } - if(mode === "edit") { - this.speciesEditor = new SpeciesEditorView({collection: this.model.species}); - }else{ - this.speciesEditor = new SpeciesViewer({collection: this.model.species}); - } + this.speciesEditor = new SpeciesEditorView({ + collection: this.model.species, + spatial: this.model.is_spatial + }); app.registerRenderSubview(this, this.speciesEditor, 'species-editor-container'); }, renderInitialConditions: function (mode="edit", opened=false) { From 7589ffc53503510a11d3ec77cd2a8ee2f50aa42c Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Sun, 13 Jun 2021 21:07:25 -0400 Subject: [PATCH 028/133] Moved the models mode section and all functions from the variables section to the model editor page. --- client/pages/model-editor.js | 71 +++++++++++++++++++++++++- client/templates/pages/modelEditor.pug | 20 ++++++++ client/tooltips.js | 13 ++--- 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 718f43c3f2..5052b1f69c 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -23,6 +23,7 @@ let path = require('path'); var app = require('../app'); var modals = require('../modals'); var tests = require('../views/tests'); +let Tooltips = require("../tooltips"); //views var PageView = require('../pages/base'); var InputView = require('../views/input'); @@ -53,6 +54,9 @@ import initPage from './page.js'; let ModelEditor = PageView.extend({ template: template, events: { + 'change [data-hook=all-continuous]' : 'setDefaultMode', + 'change [data-hook=all-discrete]' : 'setDefaultMode', + 'change [data-hook=advanced]' : 'setDefaultMode', 'click [data-hook=edit-model-help]' : function () { let modal = $(modals.operationInfoModalHtml('model-editor')).modal(); }, @@ -65,6 +69,7 @@ let ModelEditor = PageView.extend({ }, initialize: function (attrs, options) { PageView.prototype.initialize.apply(this, arguments); + this.tooltips = Tooltips.modelEditor var self = this; let urlParams = new URLSearchParams(window.location.search) var directory = urlParams.get('path'); @@ -217,6 +222,12 @@ let ModelEditor = PageView.extend({ } }, renderSubviews: function () { + if(this.model.defaultMode === "" && !this.model.is_spatial){ + this.getInitialDefaultMode(); + }else{ + let dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'} + $(this.queryByHook(dataHooks[this.model.defaultMode])).prop('checked', true) + } this.modelSettings = new TimespanSettingsView({ parent: this, model: this.model.modelSettings, @@ -292,7 +303,8 @@ let ModelEditor = PageView.extend({ } this.speciesEditor = new SpeciesEditorView({ collection: this.model.species, - spatial: this.model.is_spatial + spatial: this.model.is_spatial, + defaultMode: this.model.defaultMode }); app.registerRenderSubview(this, this.speciesEditor, 'species-editor-container'); }, @@ -423,6 +435,63 @@ let ModelEditor = PageView.extend({ clickDownloadPNGButton: function (e) { let pngButton = $('div[data-hook=preview-plot-container] a[data-title*="Download plot as a png"]')[0] pngButton.click() + }, + getInitialDefaultMode: function () { + var self = this; + if(document.querySelector('#defaultModeModal')) { + document.querySelector('#defaultModeModal').remove(); + } + let modal = $(modals.renderDefaultModeModalHtml()).modal(); + let continuous = document.querySelector('#defaultModeModal .concentration-btn'); + let discrete = document.querySelector('#defaultModeModal .population-btn'); + let dynamic = document.querySelector('#defaultModeModal .hybrid-btn'); + continuous.addEventListener('click', function (e) { + self.setInitialDefaultMode(modal, "continuous"); + }); + discrete.addEventListener('click', function (e) { + self.setInitialDefaultMode(modal, "discrete"); + }); + dynamic.addEventListener('click', function (e) { + self.setInitialDefaultMode(modal, "dynamic"); + }); + }, + setAllSpeciesModes: function (prevMode) { + let self = this; + this.model.species.forEach(function (specie) { + specie.mode = self.model.defaultMode; + self.model.species.trigger('update-species', specie.compID, specie, false, true); + }); + let switchToDynamic = (!Boolean(prevMode) || prevMode !== "dynamic") && this.model.defaultMode === "dynamic"; + let switchFromDynamic = Boolean(prevMode) && prevMode === "dynamic" && this.model.defaultMode !== "dynamic"; + // if(switchToDynamic || switchFromDynamic) { + // this.speciesEditor.(); + // } + }, + setDefaultMode: function (e) { + let self = this; + let prevMode = this.model.defaultMode; + this.model.defaultMode = e.target.dataset.name; + this.speciesEditor.defaultMode = e.target.dataset.name; + this.setAllSpeciesModes(prevMode); + this.toggleVolumeContainer(); + }, + setInitialDefaultMode: function (modal, mode) { + var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'}; + modal.modal('hide'); + $(this.queryByHook(dataHooks[mode])).prop('checked', true); + this.model.defaultMode = mode; + this.speciesEditor.defaultMode = mode; + this.setAllSpeciesModes(); + this.toggleVolumeContainer(); + }, + toggleVolumeContainer: function () { + if(!this.model.is_spatial) { + if(this.model.defaultMode === "continuous") { + $(this.queryByHook("system-volume-container")).collapse("hide"); + }else{ + $(this.queryByHook("system-volume-container")).collapse("show"); + } + } } }); diff --git a/client/templates/pages/modelEditor.pug b/client/templates/pages/modelEditor.pug index eeb3660d85..5faf898400 100644 --- a/client/templates/pages/modelEditor.pug +++ b/client/templates/pages/modelEditor.pug @@ -26,6 +26,26 @@ section.page ol.breadcrumb li.breadcrumb-item.active(aria-current="page")=this.model.name + div.card + + div.card-header.pb-0 + + h3.inline Model Mode + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.modelMode) + + div.card-body + div.row + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="all-continuous" data-name="continuous") + span Concentration + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="all-discrete" data-name="discrete") + span Population + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="advanced" data-name="dynamic") + span Hybrid Concentration/Population + div(data-hook="domain-viewer-container") div(data-hook="species-editor-container") diff --git a/client/tooltips.js b/client/tooltips.js index 4c426230a2..42e8c77241 100644 --- a/client/tooltips.js +++ b/client/tooltips.js @@ -17,6 +17,13 @@ along with this program. If not, see . */ module.exports = { + modelEditor: { + modelMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ + "using discrete (integer count) values.
Hybrid Concentration/Population - Allows a variables to be represented using continuous and/or discrete values.", + + spatialModelMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ + "using discrete (integer count) values." + }, speciesEditor: { name: "Unique identifier for Variable. Cannot share a name with other model components.", @@ -26,12 +33,6 @@ module.exports = { remove: "A variables may only be removed if it is not a part of any reaction, event assignment, or rule.", - speciesMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ - "using discrete (integer count) values.
Hybrid Concentration/Population - Allows a variables to be represented using continuous and/or discrete values.", - - spatialSpeciesMode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ - "using discrete (integer count) values.", - mode: "Concentration - Variables will only be represented using continuous (floating point) values.
Population - Variables will only be represented "+ "using discrete (integer count) values.
Hybrid Concentration/Population - Allows a variables to be represented using continuous and/or discrete values.", From 7963ffdca31a61cd42a46088c23ee057e0b485b4 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Sun, 13 Jun 2021 21:11:02 -0400 Subject: [PATCH 029/133] Merged the advanced species section with the species listing section. Updated the layout for the species section to allow the advanced species section to be merged into it. --- client/templates/includes/editSpecies.pug | 70 ++++++ client/templates/includes/speciesEditor.pug | 107 ++++---- client/views/edit-species.js | 258 ++++++++++++++++++++ client/views/species-editor.js | 182 ++++---------- 4 files changed, 412 insertions(+), 205 deletions(-) create mode 100644 client/templates/includes/editSpecies.pug create mode 100644 client/views/edit-species.js diff --git a/client/templates/includes/editSpecies.pug b/client/templates/includes/editSpecies.pug new file mode 100644 index 0000000000..78a1d0d543 --- /dev/null +++ b/client/templates/includes/editSpecies.pug @@ -0,0 +1,70 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-3 + + div.pl-2(data-hook="input-name-container") + + div.col-sm-5 + + div(data-hook="input-value-container") + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X + + div.mx-1.pl-2(data-hook="advanced-species") + + div + + h5.inline Advanced + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-species-advanced" + this.model.collection.indexOf(this.model) data-hook="collapse-advanced") + + + div.collapse(id="collapse-species-advanced" + this.model.collection.indexOf(this.model)) + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6.inline Mode + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.mode) + + div.col-sm-9 + + h6.inline Switching Settings (Hybrid Only) + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.switchValue) + + div.row.mt-3 + + div.col-sm-3(data-hook="specie-mode") + + div.col-sm-4 + + div.switching + input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-tol") + | Switching Tolerance + + div.switching + input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-min") + | Minimum Value for Switching (number of molecules) + + div.col-sm-5 + + div(data-hook="switching-tolerance") + + div(data-hook="switching-threshold") diff --git a/client/templates/includes/speciesEditor.pug b/client/templates/includes/speciesEditor.pug index 9162e167f8..6b7d5da277 100644 --- a/client/templates/includes/speciesEditor.pug +++ b/client/templates/includes/speciesEditor.pug @@ -1,92 +1,67 @@ -div#species-editor.card.card-body +div#species-editor.card - div + div.card-header.pb-0 - h3.inline Variables - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-species", data-hook="collapse") - + h3.inline.mr-3 Variables - div.collapse(class="show", id="collapse-species" data-hook="species-list-container") - p - | Time varying quantities of interest; e.g.: concentrations, populations, or counts of biochemical species, epidemological compartments, chemicals, proteins, or molecules. + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="species-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="species-edit-tab" data-toggle="tab" href="#edit-species") Edit - table.table - thead - tr - th(scope="col") - div - div.inline Name + li.nav-item + + a.nav-link.tab(data-hook="species-view-tab" data-toggle="tab" href="#view-species") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-species" data-hook="collapse") - + + div.card-body + p.mb-0 + | Time varying quantities of interest; e.g.: concentrations, populations, or counts of biochemical species, epidemological compartments, chemicals, proteins, or molecules. - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + div.collapse.tab-content(class="show" id="collapse-species" data-hook="species-list-container") - th(scope="col") - div - div.inline Initial Condition + div.tab-pane.active(id="edit-species" data-hook="edit-species") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.initialValue) + hr - th(scope="col") - div - div.inline Annotation + div.mx-1.row.head.align-items-baseline - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - - th(scope="col") - div - div.inline Remove + div.col-sm-3 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - - tbody(data-hook="specie-list") + h6.inline Name - table.table - thead - tr - th(scope="col", colspan="3") + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - div + div.col-sm-5 - div.inline Variable Mode + h6.inline Initial Condition - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.speciesMode) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.initialValue) - tbody - tr - td - input(type="radio", name="species-mode", data-hook="all-continuous", data-name="continuous") - | Concentration - td - input(type="radio", name="species-mode", data-hook="all-discrete", data-name="discrete") - | Population - td - input(type="radio", name="species-mode", data-hook="advanced", data-name="dynamic") - | Hybrid Concentration/Population - - div.collapse(data-hook="advanced-species") + div.col-sm-2 - table.table + h6.inline Annotation - thead + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - tr + div.col-sm-2 - th(scope="col") Name + h6.inline Remove - th(scope="col") - div - div.inline Mode + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.mode) - - th(scope="col" colspan="2") - div - div.inline Switching Settings (Hybrid Only) + div.my-3(data-hook="specie-list") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.switchValue) + div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable - tbody(data-hook="edit-species-mode") + button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable - div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable + div.tab-pane(id="view-species" data-hook="view-species") - button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable + hr - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-species") Save Variables + div.mx-1.row.head.align-items-baseline diff --git a/client/views/edit-species.js b/client/views/edit-species.js new file mode 100644 index 0000000000..910a820cdd --- /dev/null +++ b/client/views/edit-species.js @@ -0,0 +1,258 @@ +/* +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 . +*/ + +let $ = require('jquery'); +//support files +let app = require('../app'); +let tests = require('./tests'); +let modals = require('../modals'); +//views +let InputView = require('./input'); +let View = require('ampersand-view'); +let SelectView = require('ampersand-select-view'); +//templates +let editTemplate = require('../templates/includes/editSpecies.pug'); + +module.exports = View.extend({ + bindings: { + 'model.inUse': { + hook: 'remove', + type: 'booleanAttribute', + name: 'disabled', + }, + }, + events: { + 'change [data-hook=input-name-container]' : 'setSpeciesName', + 'change [data-hook=specie-mode]' : 'setSpeciesMode', + 'change [data-hook=switching-tol]' : 'setSwitchingType', + 'change [data-hook=switching-min]' : 'setSwitchingType', + 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', + 'click [data-hook=remove]' : 'removeSpecie', + 'click [data-hook=collapse-advanced]' : 'changeCollapseButtonText' + }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.previousName = this.model.name; + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.model.mode === null && this.model.collection.parent.defaultMode !== "") { + this.model.mode = this.model.collection.parent.defaultMode; + } + }, + render: function () { + this.template = this.viewMode ? null : editTemplate; + View.prototype.render.apply(this, arguments); + $(document).on('shown.bs.modal', function (e) { + $('[autofocus]', e.target).focus(); + }); + $(document).on('hide.bs.modal', '.modal', function (e) { + e.target.remove() + }); + if(!this.model.annotation){ + $(this.queryByHook('edit-annotation-btn')).text('Add') + } + if(this.parent.defaultMode !== "dynamic") { + $(this.queryByHook("advanced-species")).css("display", "none"); + }else{ + $(this.queryByHook("advanced-species")).css("display", "block") + } + if(this.model.isSwitchTol){ + $(this.queryByHook('switching-tol')).prop('checked', true); + }else{ + $(this.queryByHook('switching-min')).prop('checked', true); + } + this.toggleSwitchingSettings(); + this.updateInputValidation(); + }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, + editAnnotation: function () { + let self = this; + let name = this.model.name; + let annotation = this.model.annotation; + if(document.querySelector('#speciesAnnotationModal')) { + document.querySelector('#speciesAnnotationModal').remove(); + } + let modal = $(modals.annotationModalHtml("species", name, annotation)).modal(); + let okBtn = document.querySelector('#speciesAnnotationModal .ok-model-btn'); + let input = document.querySelector('#speciesAnnotationModal #speciesAnnotationInput'); + input.addEventListener("keyup", function (event) { + if(event.keyCode === 13){ + event.preventDefault(); + okBtn.click(); + } + }); + okBtn.addEventListener('click', function (e) { + self.model.annotation = input.value.trim(); + self.parent.renderEditSpeciesView(); + modal.modal('hide'); + }); + }, + removeSpecie: function () { + this.remove(); + this.collection.removeSpecie(this.model); + this.parent.toggleSpeciesCollectionError(); + }, + setSpeciesMode: function (e) { + this.model.mode = e.target.value; + this.model.collection.trigger('update-species', this.model.compID, this.model, false, false); + this.updateInputValidation(); + this.toggleSwitchingSettings(); + }, + setSpeciesName: function (e) { + if(!e.target.value.trim()) { + this.model.name = this.previousName; + this.parent.renderEditSpeciesView(); + }else{ + this.previousName = this.model.name; + this.model.collection.trigger('update-species', this.model.compID, this.model, true, false); + } + }, + setSwitchingType: function (e) { + this.model.isSwitchTol = $(this.queryByHook('switching-tol')).is(":checked"); + this.updateInputValidation(); + this.toggleSwitchingSettingsInput(); + }, + toggleSwitchingSettings: function () { + if(this.model.mode === "dynamic"){ + $(this.queryByHook('switching-tol')).prop('disabled', false); + $(this.queryByHook('switching-min')).prop('disabled', false); + this.toggleSwitchingSettingsInput(); + }else{ + $(this.queryByHook('switching-tol')).prop('disabled', true); + $(this.queryByHook('switching-min')).prop('disabled', true); + $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); + $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); + } + }, + toggleSwitchingSettingsInput: function () { + if(this.model.isSwitchTol){ + $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); + $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', false); + }else{ + $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); + $(this.queryByHook('switching-threshold')).find('input').prop('disabled', false); + } + }, + update: function () {}, + updateInputValidation: function () { + // Update validation requirements and re-run tests for inputSwitchTol. + // This removes error reporting not using switching tolerance + let shouldValidateTol = this.model.mode === "dynamic" && this.model.isSwitchTol + this.inputSwitchTol.required = shouldValidateTol; + this.inputSwitchTol.tests = shouldValidateTol ? tests.valueTests : [] + this.inputSwitchTol.runTests() + // Update validation requirements and re-run tests for inputSwitchMin. + // This removes error reporting when not using minimum value for switching. + let shouldValidateMin = this.model.mode === "dynamic" && !this.model.isSwitchTol + this.inputSwitchMin.required = shouldValidateMin; + this.inputSwitchMin.tests = shouldValidateMin ? tests.valueTests : [] + this.inputSwitchMin.runTests() + // Add/Remove 'input-invalid' class from inputSwitchTol and inputSwitchMin based on whether + // the user is using switching tolerance or minimum value for switching + let tolInput = $(this.queryByHook('switching-tolerance')).find('input')[0] + let minInput = $(this.queryByHook('switching-threshold')).find('input')[0] + if(this.model.mode !== "dynamic") { + $(tolInput).removeClass('input-invalid') + $(minInput).removeClass('input-invalid') + }else if(this.model.isSwitchTol){ + $(minInput).removeClass('input-invalid') + if(this.model.switchTol === "" || isNaN(this.model.switchTol)){ + $(tolInput).addClass('input-invalid') + } + }else{ + $(tolInput).removeClass('input-invalid') + if(this.model.switchMin === "" || isNaN(this.model.switchMin)){ + $(minInput).addClass('input-invalid') + } + } + }, + updateValid: function (e) {}, + subviews: { + inputName: { + hook: 'input-name-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'name', + tests: tests.nameTests, + modelKey: 'name', + valueType: 'string', + value: this.model.name, + }); + } + }, + inputValue: { + hook: 'input-value-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'value', + tests: tests.valueTests, + modelKey: 'value', + valueType: 'number', + value: this.model.value, + }); + } + }, + selectMode: { + hook: 'specie-mode', + prepareView: function (el) { + options = [['continuous', 'Concentration'], ['discrete', 'Population'], + ['dynamic', 'Hybrid Concentration/Population']] + return new SelectView({ + name: 'mode', + required: true, + idAttributes: 'cid', + options: options, + value: this.model.mode, + }); + } + }, + inputSwitchTol: { + hook: 'switching-tolerance', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'switching-tolerance', + tests: tests.valueTests, + modelKey: 'switchTol', + valueType: 'number', + value: this.model.switchTol, + }); + } + }, + inputSwitchMin: { + hook: 'switching-threshold', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'switching-threshold', + tests: tests.valueTests, + modelKey: 'switchMin', + valueType: 'number', + value: this.model.switchMin, + }); + } + } + } +}); diff --git a/client/views/species-editor.js b/client/views/species-editor.js index e144b43d22..fbf348b4a1 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -16,54 +16,50 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -var $ = require('jquery'); +let $ = require('jquery'); //support files let app = require('../app'); -var modals = require('../modals'); -var Tooltips = require('../tooltips'); +let Tooltips = require('../tooltips'); //views -var View = require('ampersand-view'); -var EditNonspatialSpecieView = require('./edit-specie'); -var EditSpatialSpecieView = require('./edit-spatial-specie'); -var EditAdvancedSpecie = require('./edit-advanced-specie'); +let View = require('ampersand-view'); +let SpecieView = require('./edit-species'); //templates -var nonspatialSpecieTemplate = require('../templates/includes/speciesEditor.pug'); -var spatialSpecieTemplate = require('../templates/includes/spatialSpeciesEditor.pug'); +let specieTemplate = require('../templates/includes/speciesEditor.pug'); module.exports = View.extend({ events: { - 'change [data-hook=all-continuous]' : 'getDefaultSpeciesMode', - 'change [data-hook=all-discrete]' : 'getDefaultSpeciesMode', - 'change [data-hook=advanced]' : 'getDefaultSpeciesMode', - 'click [data-hook=add-species]' : 'handleAddSpeciesClick', - 'click [data-hook=save-species]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText', + 'click [data-hook=add-species]' : 'handleAddSpeciesClick' }, initialize: function (attrs, options) { - var self = this; View.prototype.initialize.apply(this, arguments); - this.baseModel = this.collection.parent; - this.tooltips = Tooltips.speciesEditor + this.template = attrs.spatial ? null : specieTemplate; + this.tooltips = Tooltips.speciesEditor; + this.defaultMode = attrs.defaultMode; + let self = this this.collection.on('update-species', function (compID, specie, isNameUpdate, isDefaultMode) { - self.collection.parent.reactions.map(function (reaction) { - reaction.reactants.map(function (reactant) { + self.collection.parent.reactions.forEach(function (reaction) { + reaction.reactants.forEach(function (reactant) { if(reactant.specie.compID === compID) { reactant.specie = specie; } }); - reaction.products.map(function (product) { + reaction.products.forEach(function (product) { if(product.specie.compID === compID) { product.specie = specie; } }); if(isNameUpdate) { reaction.buildSummary(); + if(reaction.selected) { + self.parent.reactionsEditor.setDetailsView(reaction); + } }else if(!isDefaultMode || specie.compID === self.collection.models[self.collection.length-1].compID){ reaction.checkModes(); } }); - self.collection.parent.eventsCollection.map(function (event) { - event.eventAssignments.map(function (assignment) { + self.collection.parent.eventsCollection.forEach(function (event) { + event.eventAssignments.forEach(function (assignment) { if(assignment.variable.compID === compID) { assignment.variable = specie; } @@ -72,127 +68,20 @@ module.exports = View.extend({ event.detailsView.renderEventAssignments(); } }); - self.collection.parent.rules.map(function (rule) { + self.collection.parent.rules.forEach(function (rule) { if(rule.variable.compID === compID) { rule.variable = specie; } }); if(isNameUpdate) { - self.renderSpeciesAdvancedView(); self.parent.renderRulesView(); } }); }, render: function () { - this.template = this.parent.model.is_spatial ? spatialSpecieTemplate : nonspatialSpecieTemplate; View.prototype.render.apply(this, arguments); - var defaultMode = this.collection.parent.defaultMode; - if(defaultMode === "" && !this.collection.parent.is_spatial){ - this.getInitialDefaultSpeciesMode(); - }else{ - var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'} - $(this.queryByHook(dataHooks[this.collection.parent.defaultMode])).prop('checked', true) - if(defaultMode === "dynamic"){ - $(this.queryByHook('advanced-species')).collapse('show'); - } - } - this.renderEditSpeciesView(); - this.renderSpeciesAdvancedView(); this.toggleSpeciesCollectionError(); - }, - update: function () { - }, - updateValid: function (e) { - }, - getInitialDefaultSpeciesMode: function () { - var self = this; - if(document.querySelector('#defaultModeModal')) { - document.querySelector('#defaultModeModal').remove() - } - let modal = $(modals.renderDefaultModeModalHtml()).modal(); - let continuous = document.querySelector('#defaultModeModal .concentration-btn'); - let discrete = document.querySelector('#defaultModeModal .population-btn'); - let dynamic = document.querySelector('#defaultModeModal .hybrid-btn'); - continuous.addEventListener('click', function (e) { - self.setInitialDefaultMode(modal, "continuous"); - }); - discrete.addEventListener('click', function (e) { - self.setInitialDefaultMode(modal, "discrete"); - }); - dynamic.addEventListener('click', function (e) { - self.setInitialDefaultMode(modal, "dynamic"); - }); - }, - setInitialDefaultMode: function (modal, mode) { - var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'} - modal.modal('hide') - $(this.queryByHook(dataHooks[mode])).prop('checked', true) - this.setAllSpeciesModes(mode) - }, - getDefaultSpeciesMode: function (e) { - var self = this; - this.setAllSpeciesModes(e.target.dataset.name, function (specie) { - self.collection.trigger('update-species', specie.compID, specie, false, true) - }); - }, - setAllSpeciesModes: function (defaultMode, cb) { - this.collection.parent.defaultMode = defaultMode; - this.collection.forEach(function (specie) { - specie.mode = defaultMode - if(cb) { - cb(specie) - } - }); - if(!this.collection.parent.is_spatial) { - if(defaultMode === "continuous") { - $(this.parent.queryByHook("system-volume-container")).collapse("hide") - }else{ - $(this.parent.queryByHook("system-volume-container")).collapse("show") - } - if(defaultMode === "dynamic"){ - this.renderSpeciesAdvancedView() - $(this.queryByHook('advanced-species')).collapse('show'); - } - else{ - this.speciesAdvancedView.views[0].updateInputValidation() - $(this.queryByHook('advanced-species')).collapse('hide'); - } - } - }, - renderEditSpeciesView: function () { - if(this.editSpeciesView){ - this.editSpeciesView.remove(); - } - var editSpecieView = !this.collection.parent.is_spatial ? EditNonspatialSpecieView : EditSpatialSpecieView; - this.editSpeciesView = this.renderCollection( - this.collection, - editSpecieView, - this.queryByHook('specie-list') - ); - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - renderSpeciesAdvancedView: function () { - if(this.collection.parent.is_spatial) { - return - } - if(this.speciesAdvancedView) { - this.speciesAdvancedView.remove() - } - this.speciesAdvancedView = this.renderCollection(this.collection, EditAdvancedSpecie, this.queryByHook('edit-species-mode')); - }, - handleAddSpeciesClick: function (e) { - var self = this; - var defaultMode = this.collection.parent.defaultMode; - if(defaultMode === "" && !this.collection.parent.is_spatial){ - this.getInitialDefaultSpeciesMode(); - }else{ - this.addSpecies(); - } + this.renderEditSpeciesView(); }, addSpecies: function () { if(this.parent.model.domain.types) { @@ -213,6 +102,28 @@ module.exports = View.extend({ }); }); }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, + handleAddSpeciesClick: function (e) { + let self = this; + let defaultMode = this.collection.parent.defaultMode; + if(defaultMode === "" && !this.collection.parent.is_spatial){ + this.parent.getInitialDefaultMode(); + }else{ + this.addSpecies(); + } + }, + renderEditSpeciesView: function () { + if(this.editSpeciesView){ + this.editSpeciesView.remove(); + } + this.editSpeciesView = this.renderCollection( + this.collection, + SpecieView, + this.queryByHook('specie-list') + ); + }, toggleSpeciesCollectionError: function () { let errorMsg = $(this.queryByHook('species-collection-error')) if(this.collection.length <= 0) { @@ -222,12 +133,5 @@ module.exports = View.extend({ errorMsg.addClass('component-valid') errorMsg.removeClass('component-invalid') } - }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderSpeciesView(mode="view"); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); } -}); \ No newline at end of file +}); From 10e19b1ee890d2c3adb469a970a5818454d10764 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 08:19:13 -0400 Subject: [PATCH 030/133] Finished species mode update function when model mode is changed. --- client/pages/model-editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 5052b1f69c..48b8e5ef46 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -463,9 +463,9 @@ let ModelEditor = PageView.extend({ }); let switchToDynamic = (!Boolean(prevMode) || prevMode !== "dynamic") && this.model.defaultMode === "dynamic"; let switchFromDynamic = Boolean(prevMode) && prevMode === "dynamic" && this.model.defaultMode !== "dynamic"; - // if(switchToDynamic || switchFromDynamic) { - // this.speciesEditor.(); - // } + if(switchToDynamic || switchFromDynamic) { + this.speciesEditor.renderEditSpeciesView(); + } }, setDefaultMode: function (e) { let self = this; From e0a92164239e3fc7a2190a25b277790cf7029213 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 08:20:35 -0400 Subject: [PATCH 031/133] Styling updates for species advanced section. --- client/templates/includes/editSpecies.pug | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/templates/includes/editSpecies.pug b/client/templates/includes/editSpecies.pug index 78a1d0d543..3c1ad72082 100644 --- a/client/templates/includes/editSpecies.pug +++ b/client/templates/includes/editSpecies.pug @@ -25,11 +25,11 @@ div.mx-1 div.mx-1.pl-2(data-hook="advanced-species") - div + div.align-items-baseline - h5.inline Advanced + h6.inline Advanced - button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-species-advanced" + this.model.collection.indexOf(this.model) data-hook="collapse-advanced") + + button.btn.btn-outline-collapse.mb-2(data-toggle="collapse" data-target="#collapse-species-advanced" + this.model.collection.indexOf(this.model) data-hook="collapse-advanced") + div.collapse(id="collapse-species-advanced" + this.model.collection.indexOf(this.model)) From 981816a7abb372a4dbc8275afc466aeeb1b97532 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 09:29:43 -0400 Subject: [PATCH 032/133] Added function to update the species viewer when the model mode is changed. --- client/pages/model-editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 48b8e5ef46..012a013550 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -465,6 +465,7 @@ let ModelEditor = PageView.extend({ let switchFromDynamic = Boolean(prevMode) && prevMode === "dynamic" && this.model.defaultMode !== "dynamic"; if(switchToDynamic || switchFromDynamic) { this.speciesEditor.renderEditSpeciesView(); + this.speciesEditor.renderViewSpeciesView(); } }, setDefaultMode: function (e) { From 408ff5821d2142c444e8c7b1964b8f8edbc5e388 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 09:32:33 -0400 Subject: [PATCH 033/133] Update the species viewer to use the tab system instead of a table system. --- client/templates/includes/speciesEditor.pug | 24 +++++++++++++- client/templates/includes/viewSpecies.pug | 36 ++++++++++++--------- client/views/edit-species.js | 18 +++++++++-- client/views/species-editor.js | 26 ++++++++++++++- 4 files changed, 84 insertions(+), 20 deletions(-) diff --git a/client/templates/includes/speciesEditor.pug b/client/templates/includes/speciesEditor.pug index 6b7d5da277..0c0ae48995 100644 --- a/client/templates/includes/speciesEditor.pug +++ b/client/templates/includes/speciesEditor.pug @@ -54,7 +54,7 @@ div#species-editor.card div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) - div.my-3(data-hook="specie-list") + div.my-3(data-hook="edit-specie-list") div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable @@ -65,3 +65,25 @@ div#species-editor.card hr div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name + + div.col-sm-2 + + h6.inline Initial Condition + + div.col-sm-2 + + h6.inline Mode + + div.col-sm-4(data-hook="species-switching-header") + + h6.inline Switch Tolerance/Minimum Value for Switching + + div.col-sm-2(data-hook="species-annotation-header") + + h6.inline Annotation + + div.my-3(data-hook="view-specie-list") diff --git a/client/templates/includes/viewSpecies.pug b/client/templates/includes/viewSpecies.pug index e5ab763ec9..a792c44923 100644 --- a/client/templates/includes/viewSpecies.pug +++ b/client/templates/includes/viewSpecies.pug @@ -1,21 +1,27 @@ -tr - td=this.model.name +div.mx-1 - if this.model.collection.parent.is_spatial - td=this.model.diffusionConst + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.types.join(", ") - else - td=this.model.value + div.row - td=this.model.mode + div.col-sm-2 - if this.model.collection.parent.defaultMode === 'dynamic' - td=this.switchingValWithLabel + div.pl-2=this.model.name - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-2 - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div - \ No newline at end of file + div=this.model.value + + div.col-sm-2 + + div=this.model.mode + + if this.model.mode === "dynamic" + div.col-sm-4 + div=this.switchingValWithLabel + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/views/edit-species.js b/client/views/edit-species.js index 910a820cdd..76634d2850 100644 --- a/client/views/edit-species.js +++ b/client/views/edit-species.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ let $ = require('jquery'); +let _ = require('underscore'); //support files let app = require('../app'); let tests = require('./tests'); @@ -27,6 +28,7 @@ let View = require('ampersand-view'); let SelectView = require('ampersand-select-view'); //templates let editTemplate = require('../templates/includes/editSpecies.pug'); +let viewTemplate = require('../templates/includes/viewSpecies.pug') module.exports = View.extend({ bindings: { @@ -49,12 +51,15 @@ module.exports = View.extend({ View.prototype.initialize.apply(this, arguments); this.previousName = this.model.name; this.viewMode = attrs.viewMode ? attrs.viewMode : false; - if(this.model.mode === null && this.model.collection.parent.defaultMode !== "") { - this.model.mode = this.model.collection.parent.defaultMode; + if(this.model.mode === null && this.parent.defaultMode !== "") { + this.model.mode = this.parent.defaultMode; } + this.switchingValWithLabel = this.model.isSwitchTol ? + "Switching Tolerance: " + this.model.switchTol : + "Minimum Value For Switching: " + this.model.switchMin }, render: function () { - this.template = this.viewMode ? null : editTemplate; + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); @@ -62,6 +67,9 @@ module.exports = View.extend({ $(document).on('hide.bs.modal', '.modal', function (e) { e.target.remove() }); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)) + } if(!this.model.annotation){ $(this.queryByHook('edit-annotation-btn')).text('Add') } @@ -151,6 +159,7 @@ module.exports = View.extend({ }, update: function () {}, updateInputValidation: function () { + if(this.viewMode) {return} // Update validation requirements and re-run tests for inputSwitchTol. // This removes error reporting not using switching tolerance let shouldValidateTol = this.model.mode === "dynamic" && this.model.isSwitchTol @@ -183,6 +192,9 @@ module.exports = View.extend({ } }, updateValid: function (e) {}, + updateViewer: function () { + this.parent.renderViewSpeciesView() + }, subviews: { inputName: { hook: 'input-name-container', diff --git a/client/views/species-editor.js b/client/views/species-editor.js index fbf348b4a1..9f995292ac 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -82,6 +82,7 @@ module.exports = View.extend({ View.prototype.render.apply(this, arguments); this.toggleSpeciesCollectionError(); this.renderEditSpeciesView(); + this.renderViewSpeciesView(); }, addSpecies: function () { if(this.parent.model.domain.types) { @@ -121,7 +122,30 @@ module.exports = View.extend({ this.editSpeciesView = this.renderCollection( this.collection, SpecieView, - this.queryByHook('specie-list') + this.queryByHook('edit-specie-list') + ); + }, + renderViewSpeciesView: function () { + if(this.viewSpeciesView){ + this.viewSpeciesView.remove(); + } + if(this.defaultMode !== "dynamic") { + $(this.queryByHook("species-switching-header")).css("display", "none"); + }else{ + $(this.queryByHook("species-switching-header")).css("display", "block"); + } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("species-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("species-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {viewMode: true}}; + this.viewSpeciesView = this.renderCollection( + this.collection, + SpecieView, + this.queryByHook('view-specie-list'), + options ); }, toggleSpeciesCollectionError: function () { From b9486bc5ac60be778d06d28ccf0ee21491ad6cd0 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 09:43:09 -0400 Subject: [PATCH 034/133] Implemented read only mode for the species editor. --- client/views/model-viewer.js | 5 +++-- client/views/species-editor.js | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index 4006bc62be..a1b8cb16f4 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -22,7 +22,7 @@ let app = require('../app'); let View = require('ampersand-view'); let RulesViewer = require('./rules-viewer'); let EventsViewer = require('./events-viewer'); -let SpeciesViewer = require('./species-viewer'); +let SpeciesViewer = require('./species-editor'); let ReactionsViewer = require('./reactions-viewer'); let ParametersEditor = require('./parameters-editor'); let SBMLComponentsView = require('./sbml-component-editor'); @@ -80,7 +80,8 @@ module.exports = View.extend({ }, renderSpeciesView: function () { let speciesViewer = new SpeciesViewer({ - collection: this.model.species + collection: this.model.species, + readOnly: true }); app.registerRenderSubview(this, speciesViewer, "species-viewer-container"); }, diff --git a/client/views/species-editor.js b/client/views/species-editor.js index 9f995292ac..0449e7e7eb 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -33,6 +33,7 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.template = attrs.spatial ? null : specieTemplate; this.tooltips = Tooltips.speciesEditor; this.defaultMode = attrs.defaultMode; @@ -80,8 +81,19 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); - this.toggleSpeciesCollectionError(); - this.renderEditSpeciesView(); + if(this.readOnly) { + $(this.queryByHook('species-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('species-view-tab')).tab('show'); + $(this.queryByHook('edit-species')).removeClass('active'); + $(this.queryByHook('view-species')).addClass('active'); + }else{ + this.toggleSpeciesCollectionError(); + this.renderEditSpeciesView(); + } this.renderViewSpeciesView(); }, addSpecies: function () { From 574335dc99a01de37fe9f90f00f73e0a638b0453 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 09:50:55 -0400 Subject: [PATCH 035/133] Removed dead files. --- .../templates/includes/editAdvancedSpecie.pug | 19 -- client/templates/includes/editReactionVar.pug | 12 -- client/templates/includes/speciesViewer.pug | 28 --- client/views/edit-advanced-specie.js | 162 ------------------ client/views/edit-specie.js | 135 --------------- client/views/species-viewer.js | 54 ------ client/views/view-specie.js | 40 ----- 7 files changed, 450 deletions(-) delete mode 100644 client/templates/includes/editAdvancedSpecie.pug delete mode 100644 client/templates/includes/editReactionVar.pug delete mode 100644 client/templates/includes/speciesViewer.pug delete mode 100644 client/views/edit-advanced-specie.js delete mode 100644 client/views/edit-specie.js delete mode 100644 client/views/species-viewer.js delete mode 100644 client/views/view-specie.js diff --git a/client/templates/includes/editAdvancedSpecie.pug b/client/templates/includes/editAdvancedSpecie.pug deleted file mode 100644 index e341430071..0000000000 --- a/client/templates/includes/editAdvancedSpecie.pug +++ /dev/null @@ -1,19 +0,0 @@ -tr - - td.name: div(data-hook="specie-name")=this.model.name - - td: div(data-hook="specie-mode") - - td - div.switching - input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-tol") - | Switching Tolerance - - div.switching - input(type="radio", name=this.model.name + "-switch-method", data-hook="switching-min") - | Minimum Value for Switching (number of molecules) - - td - div(data-hook="switching-tolerance") - - div(data-hook="switching-threshold") \ No newline at end of file diff --git a/client/templates/includes/editReactionVar.pug b/client/templates/includes/editReactionVar.pug deleted file mode 100644 index 8d416852be..0000000000 --- a/client/templates/includes/editReactionVar.pug +++ /dev/null @@ -1,12 +0,0 @@ -tr - td.name: div(data-hook="input-name-container") - - td: div(data-hook="input-value-container") - - td - - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") - - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit - - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file diff --git a/client/templates/includes/speciesViewer.pug b/client/templates/includes/speciesViewer.pug deleted file mode 100644 index f2ad8b30e9..0000000000 --- a/client/templates/includes/speciesViewer.pug +++ /dev/null @@ -1,28 +0,0 @@ -div#species-viewer.card.card-body - - div - - h3.inline Variables - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-species", data-hook="collapse") - - - div.collapse(class="show", id="collapse-species") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - if this.collection.parent.is_spatial - th.col-md-3-view(scope="col") Diffusion Constant - th.col-md-3-view(scope="col") Restricted to Types: - else - th.col-md-3-view(scope="col") Initial Condition - th.col-md-3-view(scope="col") Mode - if this.collection.parent.defaultMode === "dynamic" - th.col-md-3-view(scope="col") Switch Tolerance/Minimum Value for Switching - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="specie-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-species") Edit Variables diff --git a/client/views/edit-advanced-specie.js b/client/views/edit-advanced-specie.js deleted file mode 100644 index 33c6ac446a..0000000000 --- a/client/views/edit-advanced-specie.js +++ /dev/null @@ -1,162 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -var tests = require('./tests'); -//views -var View = require('ampersand-view'); -var SelectView = require('ampersand-select-view'); -var InputView = require('./input'); -//templates -var template = require('../templates/includes/editAdvancedSpecie.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'change [data-hook=switching-tol]' : 'setSwitchingType', - 'change [data-hook=switching-min]' : 'setSwitchingType', - 'change [data-hook=specie-mode]' : 'setSpeciesMode', - }, - initialize: function () { - View.prototype.initialize.apply(this, arguments); - }, - render: function () { - View.prototype.render.apply(this, arguments); - var optionDict = {"continuous":"Concentration", "discrete":"Population", "dynamic":"Hybrid Concentration/Population"} - var modeSelectView = new SelectView({ - label: '', - name: 'mode', - required: true, - idAttributes: 'cid', - options: ['Concentration','Population','Hybrid Concentration/Population'], - value: optionDict[this.model.mode], - }); - app.registerRenderSubview(this, modeSelectView, "specie-mode") - if(this.model.isSwitchTol){ - $(this.queryByHook('switching-tol')).prop('checked', true); - }else{ - $(this.queryByHook('switching-min')).prop('checked', true); - } - this.toggleSwitchingSettings(); - this.updateInputValidation(); - }, - update: function () { - }, - updateValid: function () { - }, - setSpeciesMode: function (e) { - var value = e.target.selectedOptions.item(0).text - var modeDict = {"Concentration":"continuous","Population":"discrete","Hybrid Concentration/Population":"dynamic"} - this.model.mode = modeDict[value] - this.model.collection.trigger('update-species', this.model.compID, this.model, false, false); - this.updateInputValidation(); - this.toggleSwitchingSettings(); - }, - setSwitchingType: function (e) { - this.model.isSwitchTol = $(this.queryByHook('switching-tol')).is(":checked"); - this.updateInputValidation(); - this.toggleSwitchingSettingsInput(); - }, - updateInputValidation: function () { - // Update validation requirements and re-run tests for inputSwitchTol. - // This removes error reporting not using switching tolerance - let shouldValidateTol = this.model.mode === "dynamic" && this.model.isSwitchTol - this.inputSwitchTol.required = shouldValidateTol; - this.inputSwitchTol.tests = shouldValidateTol ? tests.valueTests : [] - this.inputSwitchTol.runTests() - // Update validation requirements and re-run tests for inputSwitchMin. - // This removes error reporting when not using minimum value for switching. - let shouldValidateMin = this.model.mode === "dynamic" && !this.model.isSwitchTol - this.inputSwitchMin.required = shouldValidateMin; - this.inputSwitchMin.tests = shouldValidateMin ? tests.valueTests : [] - this.inputSwitchMin.runTests() - // Add/Remove 'input-invalid' class from inputSwitchTol and inputSwitchMin based on whether - // the user is using switching tolerance or minimum value for switching - let tolInput = $(this.queryByHook('switching-tolerance')).find('input')[0] - let minInput = $(this.queryByHook('switching-threshold')).find('input')[0] - if(this.model.mode !== "dynamic") { - $(tolInput).removeClass('input-invalid') - $(minInput).removeClass('input-invalid') - }else if(this.model.isSwitchTol){ - $(minInput).removeClass('input-invalid') - if(this.model.switchTol === "" || isNaN(this.model.switchTol)){ - $(tolInput).addClass('input-invalid') - } - }else{ - $(tolInput).removeClass('input-invalid') - if(this.model.switchMin === "" || isNaN(this.model.switchMin)){ - $(minInput).addClass('input-invalid') - } - } - }, - toggleSwitchingSettingsInput: function () { - if(this.model.isSwitchTol){ - $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); - $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', false); - }else{ - $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); - $(this.queryByHook('switching-threshold')).find('input').prop('disabled', false); - } - }, - toggleSwitchingSettings: function () { - if(this.model.mode === "dynamic"){ - $(this.queryByHook('switching-tol')).prop('disabled', false); - $(this.queryByHook('switching-min')).prop('disabled', false); - this.toggleSwitchingSettingsInput(); - }else{ - $(this.queryByHook('switching-tol')).prop('disabled', true); - $(this.queryByHook('switching-min')).prop('disabled', true); - $(this.queryByHook('switching-threshold')).find('input').prop('disabled', true); - $(this.queryByHook('switching-tolerance')).find('input').prop('disabled', true); - } - }, - subviews: { - inputSwitchTol: { - hook: 'switching-tolerance', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'switching-tolerance', - label: '', - tests: tests.valueTests, - modelKey: 'switchTol', - valueType: 'number', - value: this.model.switchTol, - }); - }, - }, - inputSwitchMin: { - hook: 'switching-threshold', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'switching-threshold', - label: '', - tests: tests.valueTests, - modelKey: 'switchMin', - valueType: 'number', - value: this.model.switchMin, - }); - }, - }, - }, -}); \ No newline at end of file diff --git a/client/views/edit-specie.js b/client/views/edit-specie.js deleted file mode 100644 index 1408a6a67e..0000000000 --- a/client/views/edit-specie.js +++ /dev/null @@ -1,135 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -var tests = require('./tests'); -var modals = require('../modals'); -//views -var View = require('ampersand-view'); -var InputView = require('./input'); -//templates -var template = require('../templates/includes/editReactionVar.pug'); - -module.exports = View.extend({ - template: template, - bindings: { - 'model.inUse': { - hook: 'remove', - type: 'booleanAttribute', - name: 'disabled', - }, - }, - events: { - 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', - 'click [data-hook=remove]' : 'removeSpecie', - 'change [data-hook=input-name-container]' : 'setSpeciesName', - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.previousName = this.model.name - if(this.model.mode === null && this.model.collection.parent.defaultMode !== "") { - this.model.mode = this.model.collection.parent.defaultMode; - } - }, - render: function () { - View.prototype.render.apply(this, arguments); - $(document).on('shown.bs.modal', function (e) { - $('[autofocus]', e.target).focus(); - }); - $(document).on('hide.bs.modal', '.modal', function (e) { - e.target.remove() - }); - if(!this.model.annotation){ - $(this.queryByHook('edit-annotation-btn')).text('Add') - } - }, - update: function () { - }, - updateValid: function (e) { - }, - removeSpecie: function () { - this.remove(); - this.collection.removeSpecie(this.model); - this.parent.toggleSpeciesCollectionError(); - }, - setSpeciesName: function (e) { - if(!e.target.value.trim()) { - this.model.name = this.previousName; - this.parent.renderEditSpeciesView(); - }else{ - this.previousName = this.model.name; - this.model.collection.trigger('update-species', this.model.compID, this.model, true, false); - this.model.collection.trigger('remove'); - } - }, - editAnnotation: function () { - var self = this; - var name = this.model.name; - var annotation = this.model.annotation; - if(document.querySelector('#speciesAnnotationModal')) { - document.querySelector('#speciesAnnotationModal').remove(); - } - let modal = $(modals.annotationModalHtml("species", name, annotation)).modal(); - let okBtn = document.querySelector('#speciesAnnotationModal .ok-model-btn'); - let input = document.querySelector('#speciesAnnotationModal #speciesAnnotationInput'); - input.addEventListener("keyup", function (event) { - if(event.keyCode === 13){ - event.preventDefault(); - okBtn.click(); - } - }); - okBtn.addEventListener('click', function (e) { - self.model.annotation = input.value.trim(); - self.parent.renderEditSpeciesView(); - modal.modal('hide'); - }); - }, - subviews: { - inputName: { - hook: 'input-name-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'name', - label: '', - tests: tests.nameTests, - modelKey: 'name', - valueType: 'string', - value: this.model.name, - }); - }, - }, - inputValue: { - hook: 'input-value-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'value', - label: '', - tests: tests.valueTests, - modelKey: 'value', - valueType: 'number', - value: this.model.value, - }); - }, - }, - }, -}); \ No newline at end of file diff --git a/client/views/species-viewer.js b/client/views/species-viewer.js deleted file mode 100644 index 7087d36bca..0000000000 --- a/client/views/species-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewSpecie = require('./view-specie'); -//templates -var template = require('../templates/includes/speciesViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-species]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewSpecie, this.queryByHook('specie-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderSpeciesView(); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/view-specie.js b/client/views/view-specie.js deleted file mode 100644 index f0a5ee22be..0000000000 --- a/client/views/view-specie.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -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 . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewSpecies.pug'); - -module.exports = View.extend({ - template: template, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - let self = this; - this.types = []; - if(this.model.types) { - this.model.types.forEach(function (index) { - let type = self.model.collection.parent.domain.types.get(index, "typeID"); - self.types.push(type.name) - }); - } - this.switchingValWithLabel = this.model.isSwitchTol ? - "Switching Tolerance: " + this.model.switchTol : - "Minimum Value For Switching: " + this.model.switchMin - }, -}); \ No newline at end of file From 4a1f4d4dfe493346f3ccb3933fdc9a6d4f828f97 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 11:14:16 -0400 Subject: [PATCH 036/133] Added function to hide Hybrid model mode in the model mode section. --- client/pages/model-editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 012a013550..8831cb2582 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -241,6 +241,7 @@ let ModelEditor = PageView.extend({ app.registerRenderSubview(this, this.modelSettings, 'model-settings-container'); app.registerRenderSubview(this, this.modelStateButtons, 'model-state-buttons-container'); if(this.model.is_spatial) { + $(this.queryByHook("advaced-model-mode")).css("display", "none"); $(this.queryByHook("model-editor-advanced-container")).css("display", "none"); $(this.queryByHook("spatial-beta-message")).css("display", "block"); this.renderDomainViewer(); From aadc413a54a4a19b8b4d9ab7a355d2677ae1369c Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 11:51:48 -0400 Subject: [PATCH 037/133] Added spatial species editor template. --- .../includes/spatialSpeciesEditor.pug | 106 ++++++++++++------ client/templates/pages/modelEditor.pug | 2 +- client/views/species-editor.js | 6 +- 3 files changed, 79 insertions(+), 35 deletions(-) diff --git a/client/templates/includes/spatialSpeciesEditor.pug b/client/templates/includes/spatialSpeciesEditor.pug index 4adc51dfcb..472659cec4 100644 --- a/client/templates/includes/spatialSpeciesEditor.pug +++ b/client/templates/includes/spatialSpeciesEditor.pug @@ -1,45 +1,87 @@ -div#species-editor.card.card-body +div#species-editor.card - div + div.card-header.pb-0 - h3.inline Variables - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-species", data-hook="collapse") - + h3.inline.mr-3 Variables - div.collapse(class="show", id="collapse-species") + div.inline.mr-3 - p + ul.nav.nav-tabs.card-header-tabs(id="species-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="species-edit-tab" data-toggle="tab" href="#edit-species") Edit + + li.nav-item + + a.nav-link.tab(data-hook="species-view-tab" data-toggle="tab" href="#view-species") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-species" data-hook="collapse") - + + div.card-body + p.mb-0 | Time varying quantities of interest; e.g.: concentrations, populations, or counts of biochemical species, epidemological compartments, chemicals, proteins, or molecules. - table.table - thead - tr - th(scope="col") Name - th(scope="col") Diffusion Constant - th(scope="col") Restricted to Types: - th(scope="col") Remove - - tbody(data-hook="specie-list") + div.collapse.tab-content(class="show" id="collapse-species" data-hook="species-list-container") + + div.tab-pane.active(id="edit-species" data-hook="edit-species") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-2 + + h6.inline Diffusion Constant + + div.col-sm-4 + + h6.inline Restricted to Types: + + div.col-sm-2 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-sm-2 + + h6.inline Remove + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.remove) + + div.my-3(data-hook="edit-specie-list") + + div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable + + button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable + + div.tab-pane(id="view-species" data-hook="view-species") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name - table.table - thead - tr - th(scope="col", colspan="3") + div.col-sm-2 - div + h6.inline Diffusion Constant - div.inline Model Mode + div.col-sm-6 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.spatialSpeciesMode) + h6.inline Restricted to Types: - tbody - tr - td - input(type="radio", name="species-mode", data-hook="all-continuous", data-name="continuous") - | Concentration - td - input(type="radio", name="species-mode", data-hook="all-discrete", data-name="discrete") - | Population + div.col-sm-2(data-hook="species-annotation-header") - button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable + h6.inline Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-species") Save Variables \ No newline at end of file + div.my-3(data-hook="view-specie-list") diff --git a/client/templates/pages/modelEditor.pug b/client/templates/pages/modelEditor.pug index 5faf898400..50d0c1bae9 100644 --- a/client/templates/pages/modelEditor.pug +++ b/client/templates/pages/modelEditor.pug @@ -42,7 +42,7 @@ section.page div.col-sm-4 input.mr-2(type="radio" name="default-mode" data-hook="all-discrete" data-name="discrete") span Population - div.col-sm-4 + div.col-sm-4(data-hook="advaced-model-mode") input.mr-2(type="radio" name="default-mode" data-hook="advanced" data-name="dynamic") span Hybrid Concentration/Population diff --git a/client/views/species-editor.js b/client/views/species-editor.js index 0449e7e7eb..b59fdc04c4 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -24,7 +24,8 @@ let Tooltips = require('../tooltips'); let View = require('ampersand-view'); let SpecieView = require('./edit-species'); //templates -let specieTemplate = require('../templates/includes/speciesEditor.pug'); +let speciesTemplate = require('../templates/includes/speciesEditor.pug'); +let spatialSpeciesTemplate = require('../templates/includes/spatialSpeciesEditor.pug'); module.exports = View.extend({ events: { @@ -33,8 +34,9 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.spatial = attrs.spatial this.readOnly = attrs.readOnly ? attrs.readOnly : false; - this.template = attrs.spatial ? null : specieTemplate; + this.template = this.spatial ? spatialSpeciesTemplate : speciesTemplate; this.tooltips = Tooltips.speciesEditor; this.defaultMode = attrs.defaultMode; let self = this From 71774ebc3fe78e25243e810e0cb5ee2784110bc6 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 15:21:30 -0400 Subject: [PATCH 038/133] Added functions to update the tooltips. --- client/views/species-editor.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/client/views/species-editor.js b/client/views/species-editor.js index b59fdc04c4..8f9824ee20 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -109,7 +109,7 @@ module.exports = View.extend({ } this.collection.addSpecie(types); this.toggleSpeciesCollectionError() - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -133,11 +133,19 @@ module.exports = View.extend({ if(this.editSpeciesView){ this.editSpeciesView.remove(); } + let options = {viewOptions: {parent: this}}; this.editSpeciesView = this.renderCollection( this.collection, SpecieView, this.queryByHook('edit-specie-list') ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + + }); + }); }, renderViewSpeciesView: function () { if(this.viewSpeciesView){ @@ -154,13 +162,20 @@ module.exports = View.extend({ }else{ $(this.queryByHook("species-annotation-header")).css("display", "block"); } - let options = {viewOptions: {viewMode: true}}; + let options = {viewOptions: {parent: this, viewMode: true}}; this.viewSpeciesView = this.renderCollection( this.collection, SpecieView, this.queryByHook('view-specie-list'), options ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + + }); + }); }, toggleSpeciesCollectionError: function () { let errorMsg = $(this.queryByHook('species-collection-error')) From 56dc65026a929ae50ab40f97be02d3bac6c0f45a Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 15:23:11 -0400 Subject: [PATCH 039/133] Added an event trigger to update the viewer when the types are updated. --- client/views/component-types.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/views/component-types.js b/client/views/component-types.js index d5856b2693..212ef27413 100644 --- a/client/views/component-types.js +++ b/client/views/component-types.js @@ -46,5 +46,8 @@ module.exports = View.extend({ let index = this.parent.model.types.indexOf(typeID); this.parent.model.types.splice(index, 1); } + if(this.parent.modelType === "species") { + this.parent.model.trigger('change'); + } } }); \ No newline at end of file From 5d1899b68af2d83e2dc776eb64be1b315fcf4bac Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 15:24:36 -0400 Subject: [PATCH 040/133] Added the spatial species to the new species editor. --- .../templates/includes/editSpatialSpecie.pug | 9 --- .../templates/includes/editSpatialSpecies.pug | 28 +++++++++ .../templates/includes/viewSpatialSpecies.pug | 23 +++++++ client/views/edit-species.js | 60 +++++++++++++++---- client/views/model-viewer.js | 2 + 5 files changed, 100 insertions(+), 22 deletions(-) delete mode 100644 client/templates/includes/editSpatialSpecie.pug create mode 100644 client/templates/includes/editSpatialSpecies.pug create mode 100644 client/templates/includes/viewSpatialSpecies.pug diff --git a/client/templates/includes/editSpatialSpecie.pug b/client/templates/includes/editSpatialSpecie.pug deleted file mode 100644 index 44aefb3757..0000000000 --- a/client/templates/includes/editSpatialSpecie.pug +++ /dev/null @@ -1,9 +0,0 @@ -tr - - td.name: div(data-hook="input-name-container") - - td: div(data-hook="input-diffusion-coeff-container") - - td: div.row(data-hook="species-types") - - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file diff --git a/client/templates/includes/editSpatialSpecies.pug b/client/templates/includes/editSpatialSpecies.pug new file mode 100644 index 0000000000..ae78da2e75 --- /dev/null +++ b/client/templates/includes/editSpatialSpecies.pug @@ -0,0 +1,28 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-2 + + div.pl-2(data-hook="input-name-container") + + div.col-sm-2 + + div(data-hook="input-value-container") + + div.col-sm-4 + + div.row(data-hook="species-types") + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/viewSpatialSpecies.pug b/client/templates/includes/viewSpatialSpecies.pug new file mode 100644 index 0000000000..4af4396fa9 --- /dev/null +++ b/client/templates/includes/viewSpatialSpecies.pug @@ -0,0 +1,23 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-2 + + div.pl-2=this.model.name + + div.col-sm-2 + + div=this.model.diffusionConst + + div.col-sm-6 + + div=this.types.join(", ") + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/views/edit-species.js b/client/views/edit-species.js index 76634d2850..2aad005f6c 100644 --- a/client/views/edit-species.js +++ b/client/views/edit-species.js @@ -25,10 +25,13 @@ let modals = require('../modals'); //views let InputView = require('./input'); let View = require('ampersand-view'); +let TypesView = require('./component-types'); let SelectView = require('ampersand-select-view'); //templates let editTemplate = require('../templates/includes/editSpecies.pug'); -let viewTemplate = require('../templates/includes/viewSpecies.pug') +let editSpatialTemplate = require('../templates/includes/editSpatialSpecies.pug'); +let viewTemplate = require('../templates/includes/viewSpecies.pug'); +let viewSpatialTemplate = require('../templates/includes/viewSpatialSpecies.pug'); module.exports = View.extend({ bindings: { @@ -49,34 +52,46 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.modelType = "species"; this.previousName = this.model.name; this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode && this.parent.spatial) { + let self = this; + this.types = []; + if(this.model.types) { + this.model.types.forEach(function (index) { + let type = self.model.collection.parent.domain.types.get(index, "typeID"); + self.types.push(type.name); + }); + } + } if(this.model.mode === null && this.parent.defaultMode !== "") { this.model.mode = this.parent.defaultMode; } this.switchingValWithLabel = this.model.isSwitchTol ? "Switching Tolerance: " + this.model.switchTol : - "Minimum Value For Switching: " + this.model.switchMin + "Minimum Value For Switching: " + this.model.switchMin; }, render: function () { - this.template = this.viewMode ? viewTemplate : editTemplate; + if(this.parent.spatial){ + this.template = this.viewMode ? viewSpatialTemplate : editSpatialTemplate; + }else{ + this.template = this.viewMode ? viewTemplate : editTemplate; + } View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); $(document).on('hide.bs.modal', '.modal', function (e) { - e.target.remove() + e.target.remove(); }); - if(!this.viewMode){ - this.model.on('change', _.bind(this.updateViewer, this)) - } if(!this.model.annotation){ - $(this.queryByHook('edit-annotation-btn')).text('Add') + $(this.queryByHook('edit-annotation-btn')).text('Add'); } if(this.parent.defaultMode !== "dynamic") { $(this.queryByHook("advanced-species")).css("display", "none"); }else{ - $(this.queryByHook("advanced-species")).css("display", "block") + $(this.queryByHook("advanced-species")).css("display", "block"); } if(this.model.isSwitchTol){ $(this.queryByHook('switching-tol')).prop('checked', true); @@ -85,6 +100,12 @@ module.exports = View.extend({ } this.toggleSwitchingSettings(); this.updateInputValidation(); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)); + if(this.parent.spatial) { + this.renderTypes(); + } + } }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); @@ -116,6 +137,19 @@ module.exports = View.extend({ this.collection.removeSpecie(this.model); this.parent.toggleSpeciesCollectionError(); }, + renderTypes: function () { + if(this.typesView) { + this.typesView.remove(); + } + this.typesView = this.renderCollection( + this.model.collection.parent.domain.types, + TypesView, + this.queryByHook("species-types"), + {"filter": function (model) { + return model.typeID != 0; + }} + ); + }, setSpeciesMode: function (e) { this.model.mode = e.target.value; this.model.collection.trigger('update-species', this.model.compID, this.model, false, false); @@ -159,7 +193,7 @@ module.exports = View.extend({ }, update: function () {}, updateInputValidation: function () { - if(this.viewMode) {return} + if(this.viewMode || this.parent.spatial) {return} // Update validation requirements and re-run tests for inputSwitchTol. // This removes error reporting not using switching tolerance let shouldValidateTol = this.model.mode === "dynamic" && this.model.isSwitchTol @@ -216,11 +250,11 @@ module.exports = View.extend({ return new InputView({ parent: this, required: true, - name: 'value', + name: this.parent.spatial ? 'diffusion constant' : 'value', tests: tests.valueTests, - modelKey: 'value', + modelKey: this.parent.spatial ? 'diffusionConst' : 'value', valueType: 'number', - value: this.model.value, + value: this.parent.spatial ? this.model.diffusionConst : this.model.value, }); } }, diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index a1b8cb16f4..a4c8097612 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -81,6 +81,8 @@ module.exports = View.extend({ renderSpeciesView: function () { let speciesViewer = new SpeciesViewer({ collection: this.model.species, + spatial: this.model.is_spatial, + defaultMode: this.model.defaultMode, readOnly: true }); app.registerRenderSubview(this, speciesViewer, "species-viewer-container"); From 7d215bfea46e9ec87e1b63decc5e8d5bf1827abf Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 15:26:43 -0400 Subject: [PATCH 041/133] Removed dead file. --- client/views/edit-spatial-specie.js | 102 ---------------------------- 1 file changed, 102 deletions(-) delete mode 100644 client/views/edit-spatial-specie.js diff --git a/client/views/edit-spatial-specie.js b/client/views/edit-spatial-specie.js deleted file mode 100644 index 7a685800b6..0000000000 --- a/client/views/edit-spatial-specie.js +++ /dev/null @@ -1,102 +0,0 @@ -/* -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 . -*/ - -var tests = require('./tests'); -var _ = require('underscore'); -//views -var View = require('ampersand-view'); -var InputView = require('./input'); -var TypesView = require('./component-types'); -//templates -var template = require('../templates/includes/editSpatialSpecie.pug'); - -module.exports = View.extend({ - template: template, - bindings: { - 'model.inUse': { - hook: 'remove', - type: 'booleanAttribute', - name: 'disabled', - }, - }, - events: { - 'click [data-hook=remove]' : 'removeSpecie', - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.baseModel = this.model.collection.parent; - this.modelType = "species"; - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderTypes(); - }, - update: function () { - }, - updateValid: function () { - }, - removeSpecie: function () { - this.remove(); - this.collection.removeSpecie(this.model); - }, - renderTypes: function () { - if(this.typesView) { - this.typesView.remove(); - } - this.typesView = this.renderCollection( - this.baseModel.domain.types, - TypesView, - this.queryByHook("species-types"), - {"filter": function (model) { - return model.typeID != 0; - }} - ); - }, - subviews: { - inputName: { - hook: 'input-name-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'name', - label: '', - tests: tests.nameTests, - modelKey: 'name', - valueType: 'string', - value: this.model.name, - }); - }, - }, - inputValue: { - hook: 'input-diffusion-coeff-container', - prepareView: function (el) { - return new InputView({ - parent: this, - required: true, - name: 'diffusion coeff', - label: '', - tests: tests.valueTests, - modelKey: 'diffusionConst', - valueType: 'number', - value: this.model.diffusionConst, - }); - }, - }, - }, -}); \ No newline at end of file From 436991145e3cc23a5df49377568e36c1c93ba1f2 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 14 Jun 2021 16:51:49 -0400 Subject: [PATCH 042/133] Updated the layout for the advanced section and the system volume section. Added a view tab for system volume. --- client/pages/model-editor.js | 7 +++- client/templates/pages/modelEditor.pug | 54 +++++++++++++++++++------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index bdb85123e5..f71c127ae1 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -57,6 +57,7 @@ let ModelEditor = PageView.extend({ 'click [data-hook=edit-model-help]' : function () { let modal = $(modals.operationInfoModalHtml('model-editor')).modal(); }, + 'change [data-hook=edit-volume]' : 'updateVolumeViewer', 'click [data-hook=collapse-me-advanced-section]' : 'changeCollapseButtonText', 'click [data-hook=project-breadcrumb-link]' : 'handleProjectBreadcrumbClick', 'click [data-hook=toggle-preview-plot]' : 'togglePreviewPlot', @@ -368,10 +369,11 @@ let ModelEditor = PageView.extend({ valueType: 'number', value: this.model.volume, }); - app.registerRenderSubview(this, this.systemVolumeView, 'volume') + app.registerRenderSubview(this, this.systemVolumeView, 'edit-volume') if(this.model.defaultMode === "continuous") { $(this.queryByHook("system-volume-container")).collapse("hide") } + $(this.queryByHook("view-volume")).html("Volume: " + this.model.volume) }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); @@ -425,6 +427,9 @@ let ModelEditor = PageView.extend({ clickDownloadPNGButton: function (e) { let pngButton = $('div[data-hook=preview-plot-container] a[data-title*="Download plot as a png"]')[0] pngButton.click() + }, + updateVolumeViewer: function (e) { + $(this.queryByHook("view-volume")).html("Volume: " + this.model.volume) } }); diff --git a/client/templates/pages/modelEditor.pug b/client/templates/pages/modelEditor.pug index eeb3660d85..ac8fd7089b 100644 --- a/client/templates/pages/modelEditor.pug +++ b/client/templates/pages/modelEditor.pug @@ -36,9 +36,9 @@ section.page div(data-hook="reactions-editor-container") - div.card.card-body(data-hook="model-editor-advanced-container") + div.card(data-hook="model-editor-advanced-container") - div + div.card-header.pb-0 h3.inline Advanced @@ -46,26 +46,52 @@ section.page div.collapse(id="me-advanced-section", data-hook="me-advanced-section") - div(data-hook="events-editor-container") + div.card-body + + div(data-hook="events-editor-container") + + div(data-hook="rules-editor-container") + + div(data-hook="sbml-component-container") + + div.card.collapse.show(data-hook="system-volume-container") + + div.card-header.pb-0 + + h3.inline.mr-3 System Volume + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="volume-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="volume-edit-tab" data-toggle="tab" href="#edit-system-volume") Edit + + li.nav-item + + a.nav-link.tab(data-hook="volume-view-tab" data-toggle="tab" href="#view-system-volume") View + + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-system-volume", data-hook="collapse-system-volume") + - div(data-hook="rules-editor-container") + div.card-body + + p.mb-0 + | System volume affects custom propensities or stochastic reactions with two reactants. The volume sets the implicit parameter 'vol' which can be used in custom propensity functions. A single well-mixed volume is assumed. Compartments are not currently supported. In most cases this should not be changed. please review your model requirements carefully before changing this property. - div(data-hook="sbml-component-container") + div.collapse.tab-content(id="collapse-system-volume" data-hook="system-volume-section") - div.card.card-body.collapse.show(data-hook="system-volume-container") + div.tab-pane.active(id="edit-system-volume" data-hook="edit-system-volume") - div + hr - h3.inline System Volume - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-system-volume", data-hook="collapse-system-volume") + + div(data-hook="edit-volume") - div.collapse(id="collapse-system-volume" data-hook="system-volume-section") - - p System volume affects custom propensities or stochastic reactions with two reactants. The volume sets the implicit parameter 'vol' which can be used in custom propensity functions. A single well-mixed volume is assumed. Compartments are not currently supported. + div.tab-pane(id="view-system-volume" data-hook="view-system-volume") - hr + hr - div(data-hook="volume") + div(data-hook="view-volume") div(data-hook="model-settings-container") From 5884a52e2f13bdc006503f65cea8659d8e2ec0ff Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 17 Jun 2021 18:39:31 -0400 Subject: [PATCH 043/133] Added timestep size to the model template and to the load function for spatial models. --- stochss/handlers/util/stochss_spatial_model.py | 2 ++ stochss_templates/nonSpatialModelTemplate.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index 1c930fed1e..4d59c56f38 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -525,6 +525,8 @@ def load(self): self.model['name'] = self.get_name() if not self.model['defaultMode']: self.model['defaultMode'] = "discrete" + if "timestepSize" not in self.model['modelSettings'].keys(): + self.model['modelSettings']['timestepSize'] = self.model['modelSettings']['timeStep'] if "domain" not in self.model.keys() or len(self.model['domain'].keys()) < 6: self.model['domain'] = self.get_model_template()['domain'] elif "static" not in self.model['domain'].keys(): diff --git a/stochss_templates/nonSpatialModelTemplate.json b/stochss_templates/nonSpatialModelTemplate.json index 248896e2cc..514f8cd8a6 100644 --- a/stochss_templates/nonSpatialModelTemplate.json +++ b/stochss_templates/nonSpatialModelTemplate.json @@ -6,7 +6,8 @@ "volume": 1, "modelSettings": { "endSim": 20, - "timeStep": 0.05 + "timeStep": 0.05, + "timestepSize": 1e-5 }, "domain": { "size": null, From 9ceda1ab94c78f2f963418c9e4dc18114c59e305 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 17 Jun 2021 18:41:09 -0400 Subject: [PATCH 044/133] Added timestep size to the ampersand model. --- client/models/timespan-settings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/models/timespan-settings.js b/client/models/timespan-settings.js index 98ab1d7879..b467a2b125 100644 --- a/client/models/timespan-settings.js +++ b/client/models/timespan-settings.js @@ -22,7 +22,8 @@ var State = require('ampersand-state'); module.exports = State.extend({ props: { endSim: 'any', - timeStep: 'any' + timeStep: 'any', + timestepSize: 'number' }, initialize: function (attrs, options) { State.prototype.initialize.apply(this, arguments) From 61ba7d595fb3749222fb35015b05adb557651f45 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 17 Jun 2021 18:43:24 -0400 Subject: [PATCH 045/133] Added timestep size to the timespan settings. Re-designed the layout for timespan settings to use the new tabs layout. --- .../templates/includes/timespanSettings.pug | 116 +++++++++++++++--- client/views/timespan-settings.js | 17 +++ 2 files changed, 115 insertions(+), 18 deletions(-) diff --git a/client/templates/includes/timespanSettings.pug b/client/templates/includes/timespanSettings.pug index 35e7ad3b88..34401ab4e3 100644 --- a/client/templates/includes/timespanSettings.pug +++ b/client/templates/includes/timespanSettings.pug @@ -1,33 +1,113 @@ -div#preview-settings.card.card-body +div#preview-settings.card - div + div.card-header.pb-0 - h3.inline Timespan + h3.inline.mr-3 Timespan - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-preview-settings", data-hook="collapse") - + div.inline.mr-3 - div.collapse(class="show", id="collapse-preview-settings" data-hook="timespan-container") + ul.nav.nav-tabs.card-header-tabs(id="timespan-tabs") - p Timespan settings are used for the preview and all workflows created using this model. + li.nav-item - table.table + a.nav-link.tab.active(data-hook="timespan-edit-tab" data-toggle="tab" href="#edit-timespan") Edit - thead + li.nav-item - tr + a.nav-link.tab(data-hook="timespan-view-tab" data-toggle="tab" href="#view-timespan") View - th(scope="col") Simulation Time + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-preview-settings" data-hook="collapse") - - th(scope="col") - div - div.inline Sample Frequency + div.card-body - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.timeUnits) + p.mb-0 + | Timespan settings are used for the preview and all workflows created using this model. - tbody + div.collapse.tab-content(class="show" id="collapse-preview-settings" data-hook="timespan-container") - tr + div.tab-pane.active(id="edit-timespan" data-hook="edit-timespan") - td: div(data-hook="preview-time") + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6 Simulation Time + + div.col-sm-3 + + h6.inline Sample Frequency + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.timeUnits) + + div.col-sm-6 + + if this.parent.model.is_spatial + h6 Timestep Size + + div.mx-1.my-3.row + + div.col-sm-3(data-hook="preview-time") + + div.col-sm-3(data-hook="time-units") + + div.col-sm-6 + + if this.parent.model.is_spatial + div + + div.row + + div.col-sm-3 + + span.inline 1e-5 + + div.col-sm-8 + + span.mr-2.inline Current Value: + + div.inline(data-hook="timestep-size-value") + + div.col-sm-1.d-flex.justify-content-end + + span.inline 1 + + input.custom-range( + type="range" + min="1e-5" + max="1" + step="1e-5" + value=this.model.timestepSize + data-hook="timestep-size-slider" + ) + + div.tab-pane(id="view-timespan" data-hook="view-timespan") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + h6 Simulation Time + + div.col-sm-3 + + h6 Sample Frequency + + div.col-sm-3 + + if this.parent.model.is_spatial + h6 Timestep Size + + div.mx-1.my-3.row + + div.col-sm-3(data-hook="view-end-sim")="0 to " + this.model.endSim + + div.col-sm-3(data-hook="view-time-step")=this.model.timeStep + + if this.parent.model.is_spatial + div.col-sm-6(data-hook="view-timestep-size")=this.model.timestepSize + - td: div(data-hook="time-units") diff --git a/client/views/timespan-settings.js b/client/views/timespan-settings.js index 22b8cd3fbf..e320f36cf6 100644 --- a/client/views/timespan-settings.js +++ b/client/views/timespan-settings.js @@ -31,6 +31,10 @@ module.exports = View.extend({ template: template, events: { 'click [data-hook=collapse]' : 'changeCollapseButtonText', + 'input [data-hook=timestep-size-slider]' : 'viewTimestepValue', + 'change [data-hook=preview-time]' : 'updateViewer', + 'change [data-hook=time-units]' : 'updateViewer', + 'change [data-hook=timestep-size-slider]' : 'setTimestepSize' }, bindings: { }, @@ -40,6 +44,7 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); + $(this.queryByHook("timestep-size-value")).html(this.model.timestepSize); }, update: function (e) { }, @@ -49,6 +54,18 @@ module.exports = View.extend({ changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, + setTimestepSize: function (e) { + this.model.timestepSize = Number(e.target.value); + $(this.queryByHook("view-timestep-size")).html(this.model.timestepSize); + }, + updateViewer: function (e) { + $(this.queryByHook("view-end-sim")).html("0 to " + this.model.endSim); + $(this.queryByHook("view-time-step")).html(this.model.timeStep); + }, + viewTimestepValue: function (e) { + let value = e.target.value; + $(this.queryByHook("timestep-size-value")).html(value); + }, subviews: { inputSimEnd: { hook: 'preview-time', From b87be786eb3c6c770e7aa84efed291d7c8d59d2a Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 09:06:58 -0400 Subject: [PATCH 046/133] Added the timestep size to the model when converting a model to a spatial model. --- stochss/handlers/util/stochss_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stochss/handlers/util/stochss_model.py b/stochss/handlers/util/stochss_model.py index bb2b9ba9d9..6b617b2385 100644 --- a/stochss/handlers/util/stochss_model.py +++ b/stochss/handlers/util/stochss_model.py @@ -380,6 +380,8 @@ def convert_to_spatial(self): if self.model is None: model = self.load() model['is_spatial'] = True + if "timestepSize" not in self.model['modelSettings'].keys(): + self.model['modelSettings']['timestepSize'] = self.model['modelSettings']['timeStep'] if "domain" not in model.keys(): model['domain'] = self.get_model_template()['domain'] for species in model['species']: From 9976fae07a2553c9e1be4747703dd7b8993ccbe5 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 09:08:54 -0400 Subject: [PATCH 047/133] Refactored the timespans for notebooks and spatial previews to include the timestep size. --- stochss/handlers/util/stochss_notebook.py | 9 +++++---- stochss/handlers/util/stochss_spatial_model.py | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stochss/handlers/util/stochss_notebook.py b/stochss/handlers/util/stochss_notebook.py index baaadd017b..41840f149e 100644 --- a/stochss/handlers/util/stochss_notebook.py +++ b/stochss/handlers/util/stochss_notebook.py @@ -769,13 +769,14 @@ def __create_stoich_spec_string(self, stoich_species): def __create_tspan_string(self, model, pad): end = self.s_model['modelSettings']['endSim'] - step = self.s_model['modelSettings']['timeStep'] + output_freq = self.s_model['modelSettings']['timeStep'] + step_size = self.s_model['modelSettings']['timestepSize'] tspan = ["", f"{pad}# Timespan"] - ts_str = f'{pad}self.timespan(np.arange(0, {end + step}, {step})' if self.s_model['is_spatial']: - ts_str += f", timestep_size={step})" + ts_str = f'{pad}self.timespan(np.arange(0, {end + step_size}, {output_freq})' + ts_str += f", timestep_size={step_size})" else: - ts_str += ")" + ts_str = f'{pad}self.timespan(np.arange(0, {end + output_freq}, {output_freq}))' tspan.append(ts_str) model.extend(tspan) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index 4d59c56f38..b667f7b020 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -169,8 +169,9 @@ def __convert_initial_conditions(self, model): def __convert_model_settings(self, model): try: end = self.model['modelSettings']['endSim'] - step_size = self.model['modelSettings']['timeStep'] - tspan = numpy.arange(0, end + step_size, step_size) + output_freq = self.model['modelSettings']['timeStep'] + step_size = self.model['modelSettings']['timestepSize'] + tspan = numpy.arange(0, end + step_size, output_freq) model.timespan(tspan, timestep_size=step_size) except KeyError as err: message = "Spatial model settings are not properly formatted or " From 0c5858ae8cf83acde921e915ee69b4de2b8a97ed Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 09:09:54 -0400 Subject: [PATCH 048/133] Added the timestep size to the workflow settings template. --- stochss_templates/workflowSettingsTemplate.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stochss_templates/workflowSettingsTemplate.json b/stochss_templates/workflowSettingsTemplate.json index 85bfd2de1a..b1a402f656 100644 --- a/stochss_templates/workflowSettingsTemplate.json +++ b/stochss_templates/workflowSettingsTemplate.json @@ -19,6 +19,7 @@ }, "timespanSettings": { "endSim": 20, - "timeStep": 0.05 + "timeStep": 0.05, + "timestepSize": 1e-5 } } \ No newline at end of file From 5ce229f639a3bbe829e6dce47afda8045fbd6ab1 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 09:32:00 -0400 Subject: [PATCH 049/133] Implemented read only for the timespan settings. --- client/views/timespan-settings.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/client/views/timespan-settings.js b/client/views/timespan-settings.js index e320f36cf6..fa5f87cb4b 100644 --- a/client/views/timespan-settings.js +++ b/client/views/timespan-settings.js @@ -36,14 +36,23 @@ module.exports = View.extend({ 'change [data-hook=time-units]' : 'updateViewer', 'change [data-hook=timestep-size-slider]' : 'setTimestepSize' }, - bindings: { - }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.modelSettings }, render: function () { View.prototype.render.apply(this, arguments); + if(this.readOnly) { + $(this.queryByHook('timespan-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('timespan-view-tab')).tab('show'); + $(this.queryByHook('edit-timespan')).removeClass('active'); + $(this.queryByHook('view-timespan')).addClass('active'); + } $(this.queryByHook("timestep-size-value")).html(this.model.timestepSize); }, update: function (e) { From 7421f7c312e9a9d44cfec40422cf9e939c53ce76 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 13:24:28 -0400 Subject: [PATCH 050/133] Redesigned the event assignment layoutto use a grid layout instead of a table layout. --- .../includes/editEventAssignment.pug | 19 ++++-- .../includes/eventAssignmentsEditor.pug | 59 ++++++++++++++----- .../includes/viewEventAssignment.pug | 14 +++++ client/views/edit-event-assignment.js | 13 ++-- client/views/event-assignments-editor.js | 22 ++++++- 5 files changed, 98 insertions(+), 29 deletions(-) create mode 100644 client/templates/includes/viewEventAssignment.pug diff --git a/client/templates/includes/editEventAssignment.pug b/client/templates/includes/editEventAssignment.pug index 00c9294936..ecc8b48dda 100644 --- a/client/templates/includes/editEventAssignment.pug +++ b/client/templates/includes/editEventAssignment.pug @@ -1,7 +1,18 @@ -tr +div.mx-1 - td: div(data-hook="event-assignment-variable") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td: div(data-hook="event-assignment-Expression") + div.row - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file + div.col-sm-3 + + div.pl-2(data-hook="event-assignment-variable") + + div.col-sm-7 + + div(data-hook="event-assignment-expression") + + div.col-sm-2 + + button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/eventAssignmentsEditor.pug b/client/templates/includes/eventAssignmentsEditor.pug index 022cef0cd7..a051b943c1 100644 --- a/client/templates/includes/eventAssignmentsEditor.pug +++ b/client/templates/includes/eventAssignmentsEditor.pug @@ -1,26 +1,53 @@ -div +div#event-assignments-editor - table.table + div - thead + h5.inline.mr-3 - tr + div + + div.inline Assignments - th(scope="col") - div - div.inline Target + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignments) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.variable) + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-event-assignments" data-hook="collapse") - - th(scope="col") - div - div.inline Expression + div.collapse.show.tab-content(id="collapse-event-assignments" data-hook="event-assignments-list-container") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) + div.tab-pane.active(id="edit-event-assignments" data-hook="edit-event-assignments") - th(scope="col") Remove + hr - tbody(data-hook="event-assignments-container") + div.mx-1.row.head.align-items-baseline - button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") - | Add Assignment \ No newline at end of file + div.col-sm-3 + + div.inline Target + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.variable) + + div.col-sm-7 + + h6.inline Expression + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) + + div.col-sm-2 + + h6 Remove + + div.my-3(data-hook="edit-event-assignments-container") + + button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") Add Assignment + + div.tab-pane(id="view-event-assignments" data-hook="view-event-assignments") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 Target + + div.col-sm-7 Expression + + div.my-3(data-hook="view-event-assignments-container") diff --git a/client/templates/includes/viewEventAssignment.pug b/client/templates/includes/viewEventAssignment.pug new file mode 100644 index 0000000000..dc2a1867fc --- /dev/null +++ b/client/templates/includes/viewEventAssignment.pug @@ -0,0 +1,14 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-3 + + div.pl-2=this.model.variable.name + + div.col-sm-7 + + div=this.model.expression diff --git a/client/views/edit-event-assignment.js b/client/views/edit-event-assignment.js index 72c4b268d3..de3a46cf3b 100644 --- a/client/views/edit-event-assignment.js +++ b/client/views/edit-event-assignment.js @@ -24,18 +24,20 @@ var View = require('ampersand-view'); var InputView = require('./input'); var SelectView = require('ampersand-select-view'); //templates -var template = require('../templates/includes/editEventAssignment.pug'); +var editTemplate = require('../templates/includes/editEventAssignment.pug'); +var viewTemplate = require('../templates/includes/viewEventAssignment.pug'); module.exports = View.extend({ - template: template, events: { 'click [data-hook=remove]' : 'removeAssignment', 'change [data-hook=event-assignment-variable]' : 'selectAssignmentVariable', }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); var options = this.getOptions(); var variableSelectView = new SelectView({ @@ -47,8 +49,6 @@ module.exports = View.extend({ value: this.model.variable.name, }); app.registerRenderSubview(this, variableSelectView, 'event-assignment-variable'); - var inputField = this.queryByHook('event-assignment-Expression').children[0].children[1]; - $(inputField).attr("placeholder", "---No Expression Entered---"); }, update: function () { }, @@ -87,14 +87,13 @@ module.exports = View.extend({ }, subviews: { inputAssignmentExpression: { - hook: 'event-assignment-Expression', + hook: 'event-assignment-expression', prepareView: function (el) { return new InputView({ parent: this, required: true, name: 'event-assignment-expression', - label: '', - tests: '', + placehonder: "---No Expression Entered---", modelKey: 'expression', valueType: 'string', value: this.model.expression, diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index 917820a137..d604e6ec19 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -32,10 +32,28 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); - this.renderCollection( + this.renderEditEventAssignment(); + this.renderViewEventAssignment(); + }, + renderEditEventAssignment: function () { + if(this.editEventAssignments) { + this.editEventAssignments.remove(); + } + this.editEventAssignments = this.renderCollection( + this.collection, + EditEventAssignment, + this.queryByHook('edit-event-assignments-container') + ); + }, + renderViewEventAssignment: function () { + if(this.viewEventAssignments) { + this.viewEventAssignments.remove(); + } + let options = {viewOptions: {viewMode: true}} + this.viewEventAssignments = this.renderCollection( this.collection, EditEventAssignment, - this.queryByHook('event-assignments-container') + this.queryByHook('view-event-assignments-container') ); }, update: function () { From 20a642b84a844bf94f9e9b3cbb11e3db680b1a9e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 13:42:35 -0400 Subject: [PATCH 051/133] Implemented read only mode for event assignments. --- .../includes/eventAssignmentsEditor.pug | 42 ++++++++++--------- client/views/event-assignments-editor.js | 14 +++++-- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/client/templates/includes/eventAssignmentsEditor.pug b/client/templates/includes/eventAssignmentsEditor.pug index a051b943c1..05ab00945f 100644 --- a/client/templates/includes/eventAssignmentsEditor.pug +++ b/client/templates/includes/eventAssignmentsEditor.pug @@ -12,42 +12,44 @@ div#event-assignments-editor button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-event-assignments" data-hook="collapse") - - div.collapse.show.tab-content(id="collapse-event-assignments" data-hook="event-assignments-list-container") + div + + div.collapse.show.tab-content(id="collapse-event-assignments" data-hook="event-assignments-list-container") - div.tab-pane.active(id="edit-event-assignments" data-hook="edit-event-assignments") + div.tab-pane.active(id="edit-event-assignments" data-hook="edit-event-assignments") - hr + hr - div.mx-1.row.head.align-items-baseline + div.mx-1.row.head.align-items-baseline - div.col-sm-3 + div.col-sm-3 - div.inline Target + div.inline Target - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.variable) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.variable) - div.col-sm-7 + div.col-sm-7 - h6.inline Expression + h6.inline Expression - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) - div.col-sm-2 + div.col-sm-2 - h6 Remove + h6 Remove - div.my-3(data-hook="edit-event-assignments-container") + div.my-3(data-hook="edit-event-assignments-container") - button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") Add Assignment + button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") Add Assignment - div.tab-pane(id="view-event-assignments" data-hook="view-event-assignments") + div.tab-pane(id="view-event-assignments" data-hook="view-event-assignments") - hr + hr - div.mx-1.row.head.align-items-baseline + div.mx-1.row.head.align-items-baseline - div.col-sm-3 Target + div.col-sm-3 Target - div.col-sm-7 Expression + div.col-sm-7 Expression - div.my-3(data-hook="view-event-assignments-container") + div.my-3(data-hook="view-event-assignments-container") diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index d604e6ec19..6712e259bf 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +let $ = require('jquery'); //views var View = require('ampersand-view'); var EditEventAssignment = require('./edit-event-assignment'); @@ -29,11 +30,17 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : true; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEditEventAssignment(); - this.renderViewEventAssignment(); + if(this.readOnly) { + $(this.queryByHook('edit-event-assignments')).removeClass('active'); + $(this.queryByHook('view-event-assignments')).addClass('active'); + this.renderViewEventAssignment(); + }else{ + this.renderEditEventAssignment(); + } }, renderEditEventAssignment: function () { if(this.editEventAssignments) { @@ -53,7 +60,8 @@ module.exports = View.extend({ this.viewEventAssignments = this.renderCollection( this.collection, EditEventAssignment, - this.queryByHook('view-event-assignments-container') + this.queryByHook('view-event-assignments-container'), + options ); }, update: function () { From 33410d1e0dd7153ae8d428154e167f2aab0b5bc2 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 13:56:34 -0400 Subject: [PATCH 052/133] Updated the variable select view for event assignments. --- client/views/edit-event-assignment.js | 39 +++++++++++++++--------- client/views/event-assignments-editor.js | 2 +- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/client/views/edit-event-assignment.js b/client/views/edit-event-assignment.js index de3a46cf3b..01fdf954f9 100644 --- a/client/views/edit-event-assignment.js +++ b/client/views/edit-event-assignment.js @@ -39,16 +39,6 @@ module.exports = View.extend({ render: function () { this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); - var options = this.getOptions(); - var variableSelectView = new SelectView({ - label: '', - name: 'variable', - required: true, - idAttributes: 'cid', - options: options, - value: this.model.variable.name, - }); - app.registerRenderSubview(this, variableSelectView, 'event-assignment-variable'); }, update: function () { }, @@ -62,22 +52,28 @@ module.exports = View.extend({ getOptions: function () { var species = this.model.collection.parent.collection.parent.species; var parameters = this.model.collection.parent.collection.parent.parameters; - var speciesNames = species.map(function (specie) { return specie.name }); - var parameterNames = parameters.map(function (parameter) { return parameter.name }); - return speciesNames.concat(parameterNames); + var specs = species.map(function (specie) { + return [specie.compID, specie.name] + }); + var params = parameters.map(function (parameter) { + return [parameter.compID, parameter.name] + }); + let options = [{groupName: "Variables", options: specs}, + {groupName: "Parameters", options: params}] + return options; }, selectAssignmentVariable: function (e) { var species = this.model.collection.parent.collection.parent.species; var parameters = this.model.collection.parent.collection.parent.parameters; var val = e.target.selectedOptions.item(0).text; var eventVar = species.filter(function (specie) { - if(specie.name === val) { + if(specie.compID === val) { return specie; } }); if(!eventVar.length) { eventVar = parameters.filter(function (parameter) { - if(parameter.name === val) { + if(parameter.compID === val) { return parameter; } }); @@ -100,5 +96,18 @@ module.exports = View.extend({ }); }, }, + variableSelectView: { + hook: 'event-assignment-variable', + prepareView: function (el) { + let options = this.getOptions(); + return new SelectView({ + name: 'variable', + required: true, + idAttributes: 'cid', + groupOptions: options, + value: this.model.variable.compID, + }); + } + } }, }); \ No newline at end of file diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index 6712e259bf..64089b0f55 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -30,7 +30,7 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); - this.readOnly = attrs.readOnly ? attrs.readOnly : true; + this.readOnly = attrs.readOnly ? attrs.readOnly : false; }, render: function () { View.prototype.render.apply(this, arguments); From 0e94e1214cafc500c9070e6d2e1dfca629d5c64e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 14:00:53 -0400 Subject: [PATCH 053/133] Removed dead files. --- .../includes/eventAssignmentsViewer.pug | 11 ------ .../includes/viewEventAssignments.pug | 5 --- client/views/event-assignments-viewer.js | 34 ------------------- client/views/view-event-assignments.js | 26 -------------- 4 files changed, 76 deletions(-) delete mode 100644 client/templates/includes/eventAssignmentsViewer.pug delete mode 100644 client/templates/includes/viewEventAssignments.pug delete mode 100644 client/views/event-assignments-viewer.js delete mode 100644 client/views/view-event-assignments.js diff --git a/client/templates/includes/eventAssignmentsViewer.pug b/client/templates/includes/eventAssignmentsViewer.pug deleted file mode 100644 index 4a3aa846c8..0000000000 --- a/client/templates/includes/eventAssignmentsViewer.pug +++ /dev/null @@ -1,11 +0,0 @@ -table.table - - thead - - tr - - th(scope="col") Target - - th(scope="col") Expression - - tbody(data-hook="view-event-assignments-list") \ No newline at end of file diff --git a/client/templates/includes/viewEventAssignments.pug b/client/templates/includes/viewEventAssignments.pug deleted file mode 100644 index 6d9f62e879..0000000000 --- a/client/templates/includes/viewEventAssignments.pug +++ /dev/null @@ -1,5 +0,0 @@ -tr - - td=this.model.variable.name - - td=this.model.expression \ No newline at end of file diff --git a/client/views/event-assignments-viewer.js b/client/views/event-assignments-viewer.js deleted file mode 100644 index ee3551a067..0000000000 --- a/client/views/event-assignments-viewer.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -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 . -*/ - -//views -var View = require('ampersand-view'); -var ViewAssignments = require('./view-event-assignments'); -//templates -var template = require('../templates/includes/eventAssignmentsViewer.pug'); - -module.exports = View.extend({ - template: template, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewAssignments, 'view-event-assignments-list'); - }, -}); \ No newline at end of file diff --git a/client/views/view-event-assignments.js b/client/views/view-event-assignments.js deleted file mode 100644 index 709bab1ce8..0000000000 --- a/client/views/view-event-assignments.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -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 . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewEventAssignments.pug'); - -module.exports = View.extend({ - template: template, -}); \ No newline at end of file From f15c33b970b35462c2622a84e798b7b7c62f59a1 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 18 Jun 2021 14:06:03 -0400 Subject: [PATCH 054/133] Moved the event assignments header into the event assignment editor template. --- client/templates/includes/eventDetails.pug | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/templates/includes/eventDetails.pug b/client/templates/includes/eventDetails.pug index 6ccdb91b55..2064127c44 100644 --- a/client/templates/includes/eventDetails.pug +++ b/client/templates/includes/eventDetails.pug @@ -69,10 +69,4 @@ div(data-hook="event-details") input(type="radio", name="use-values-from" data-hook="assignment-time" data-name="assignment") | Assignment Time - h5.inline - div - div.inline Assignments - - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.assignments) - div(data-hook="event-assignments") \ No newline at end of file From c5359621812b1988bd89535cda7e162d51d37be4 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 10:50:04 -0400 Subject: [PATCH 055/133] Re-designed the edit layout for events. --- client/templates/includes/eventDetails.pug | 6 +- client/templates/includes/eventListings.pug | 23 ++++--- client/templates/includes/eventsEditor.pug | 69 +++++++++++++-------- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/client/templates/includes/eventDetails.pug b/client/templates/includes/eventDetails.pug index 2064127c44..f84bd82a05 100644 --- a/client/templates/includes/eventDetails.pug +++ b/client/templates/includes/eventDetails.pug @@ -8,7 +8,9 @@ div(data-hook="event-details") div.col-md-8.inline(id="event-trigger-expression" data-hook="event-trigger-expression") - div + div(data-hook="event-assignments") + + div.my-1 h6.inline Advanced button.btn.btn-outline-collapse(data-toggle='collapse', data-target='#advanced-events', data-hook='advanced-event-button') + @@ -68,5 +70,3 @@ div(data-hook="event-details") input(type="radio", name="use-values-from" data-hook="assignment-time" data-name="assignment") | Assignment Time - - div(data-hook="event-assignments") \ No newline at end of file diff --git a/client/templates/includes/eventListings.pug b/client/templates/includes/eventListings.pug index c1d7a49696..f67a2ecc19 100644 --- a/client/templates/includes/eventListings.pug +++ b/client/templates/includes/eventListings.pug @@ -1,13 +1,22 @@ -tr +div.mx-1 - td: input(type="radio", data-hook="select", name="event-select") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td.name: div(data-hook="event-name-container") + div.row.align-items-baseline - td + div.col-md-2 - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.pl-3: input(type="radio", data-hook="select", name="event-select") - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + div.col-md-5(data-hook="event-name-container") - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file + div.col-md-3 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-md-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/eventsEditor.pug b/client/templates/includes/eventsEditor.pug index 41f5845440..b5d69cf9d0 100644 --- a/client/templates/includes/eventsEditor.pug +++ b/client/templates/includes/eventsEditor.pug @@ -1,45 +1,64 @@ -div#events.card.card-body +div#events.card - div + div.card-header.pb-0 - h3.inline Events + h3.inline.mr-3 Events + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="events-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="events-edit-tab" data-toggle="tab" href="#edit-events") Edit + + li.nav-item + + a.nav-link.tab(data-hook="events-view-tab" data-toggle="tab" href="#view-events") View button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#events-container", data-hook="collapse") + - div.collapse(id="events-container", data-hook="events") + div.card-body + + p.mb-0 + | A discontinuous action triggered by the state of the system. + + div.collapse.tab-content(id="events-container", data-hook="events") + + div.tab-pane.active(id="edit-events" data-hook="edit-events") + + div.row.mb-3 + + div.col-md-6.container-part + + hr - div.row + div.mx-1.row.head.align-items-baseline - div.col-md-6.container-part - p - | A discontinuous action triggered by the state of the system. + div.col-md-2 - table.table + h6 Edit - thead + div.col-md-5 - tr + h6.inline Name - th(scope="col") Edit + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - th(scope="col") - div - div.inline Name + div.col-md-3 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + h6.inline Annotation - th(scope="col") - div - div.inline Annotation + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.col-md-2 - th(scope="col") Remove + h6 Remove - tbody(data-hook="event-listing-container") + div.mt-3(data-hook="event-listing-container") - div.col-md-6.container-part(data-hook="event-details-container") + div.col-md-6.container-part(data-hook="event-details-container") - button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event + button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-events") Save Events + div.tab-pane(id="view-events" data-hook="view-events") From 1b6dba5cebec34286de2f70ccb4834031c76db79 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 12:42:24 -0400 Subject: [PATCH 056/133] Setup the new view mode for events. --- .../includes/eventAssignmentsEditor.pug | 8 +- client/templates/includes/eventsEditor.pug | 30 +++++++- client/templates/includes/viewEvents.pug | 54 ++++++++------ client/views/event-assignments-editor.js | 2 + client/views/event-details.js | 1 + client/views/event-listings.js | 73 ++++++++++--------- client/views/events-editor.js | 40 ++++++---- 7 files changed, 130 insertions(+), 78 deletions(-) diff --git a/client/templates/includes/eventAssignmentsEditor.pug b/client/templates/includes/eventAssignmentsEditor.pug index 05ab00945f..ea9fceb423 100644 --- a/client/templates/includes/eventAssignmentsEditor.pug +++ b/client/templates/includes/eventAssignmentsEditor.pug @@ -1,6 +1,6 @@ div#event-assignments-editor - div + div(data-hook="event-assignments-header") h5.inline.mr-3 @@ -8,7 +8,7 @@ div#event-assignments-editor div.inline Assignments - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignments) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.assignments) button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-event-assignments" data-hook="collapse") - @@ -26,13 +26,13 @@ div#event-assignments-editor div.inline Target - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.variable) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) div.col-sm-7 h6.inline Expression - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.assignmentExpression) div.col-sm-2 diff --git a/client/templates/includes/eventsEditor.pug b/client/templates/includes/eventsEditor.pug index b5d69cf9d0..0828d3d9ee 100644 --- a/client/templates/includes/eventsEditor.pug +++ b/client/templates/includes/eventsEditor.pug @@ -27,7 +27,7 @@ div#events.card div.tab-pane.active(id="edit-events" data-hook="edit-events") - div.row.mb-3 + div.row.mb-3.align-items-baseline div.col-md-6.container-part @@ -55,10 +55,36 @@ div#events.card h6 Remove - div.mt-3(data-hook="event-listing-container") + div.mt-3(data-hook="edit-event-listing-container") div.col-md-6.container-part(data-hook="event-details-container") button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event div.tab-pane(id="view-events" data-hook="view-events") + + hr + + div.mx-1.row.head + + div.col-sm-2 + + h6 Name + + div.col-sm-2 + + h6 Trigger + + div.col-sm-4 + + h6 Assignments + + div.col-sm-2 + + h6 Advanced + + div.col-sm-2(data-hook="events-annotation-header") + + h6 Annotation + + div.mt-3(data-hook="view-event-listing-container") diff --git a/client/templates/includes/viewEvents.pug b/client/templates/includes/viewEvents.pug index a26dfb151f..6f819a62e8 100644 --- a/client/templates/includes/viewEvents.pug +++ b/client/templates/includes/viewEvents.pug @@ -1,45 +1,51 @@ -tr +div.mx-1 - td: div.name=this.model.name + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.model.triggerExpression + div.row - td: div(data-hook="assignment-viewer") + div.col-sm-2 - td + div.pl-2=this.model.name - div="Delay: "+this.delay + div.col-sm-2=this.model.triggerExpression - div="Priority: "+this.model.priority + div.col-sm-4(data-hook="assignment-viewer") - div + div.col-sm-2 - span.checkbox(for="initial-value") Initial Value: + div="Delay: "+this.delay - input(type="checkbox", id="initial-value", data-hook="event-trigger-init-value" disabled) + div="Priority: "+this.model.priority - div + div - span.checkbox(for="persistent") Persistent: + span.checkbox(for="initial-value") Initial Value: - input(type="checkbox", id="persistent", data-hook="event-trigger-persistent" disabled) + input(type="checkbox", id="initial-value", data-hook="event-trigger-init-value" disabled) - div.event-adv-header Use Values From + div - div + span.checkbox(for="persistent") Persistent: - input(type="radio", id="trigger-time", name="use-values-from" data-hook="trigger-time" disabled) + input(type="checkbox", id="persistent", data-hook="event-trigger-persistent" disabled) - span.inline.horizontal-space(for="trigger-time") Trigger Time + div.event-adv-header Use Values From - div + div - input(type="radio", id="assignment-time", name="use-values-from" data-hook="assignment-time" disabled) + input(type="radio", id="trigger-time", name="use-values-from" data-hook="trigger-time" disabled) - span.inline.horizontal-space(for="assignment-time") Assignment Time + span.inline.horizontal-space(for="trigger-time") Trigger Time - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + input(type="radio", id="assignment-time", name="use-values-from" data-hook="assignment-time" disabled) + + span.inline.horizontal-space(for="assignment-time") Assignment Time + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index 64089b0f55..41769b0d3a 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -31,12 +31,14 @@ module.exports = View.extend({ initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.tooltips = attrs.tooltips; }, render: function () { View.prototype.render.apply(this, arguments); if(this.readOnly) { $(this.queryByHook('edit-event-assignments')).removeClass('active'); $(this.queryByHook('view-event-assignments')).addClass('active'); + $(this.queryByHook('event-assignments-header')).css("display", "none"); this.renderViewEventAssignment(); }else{ this.renderEditEventAssignment(); diff --git a/client/views/event-details.js b/client/views/event-details.js index 3d64cdb23d..0c35e2f8f1 100644 --- a/client/views/event-details.js +++ b/client/views/event-details.js @@ -81,6 +81,7 @@ module.exports = View.extend({ } this.eventAssignmentsView = new EventAssignment({ collection: this.model.eventAssignments, + tooltips: this.parent.tooltips }); app.registerRenderSubview(this, this.eventAssignmentsView, 'event-assignments'); }, diff --git a/client/views/event-listings.js b/client/views/event-listings.js index 2f248816f7..712e5a0c60 100644 --- a/client/views/event-listings.js +++ b/client/views/event-listings.js @@ -18,51 +18,34 @@ along with this program. If not, see . var $ = require('jquery'); //support files +let app = require('../app'); var tests = require('./tests'); var modals = require('../modals'); //views var View = require('ampersand-view'); var InputView = require('./input'); +var EventAssignment = require('./event-assignments-editor'); //templates -var template = require('../templates/includes/eventListings.pug'); - -let eventAnnotationModalHtml = (eventName, annotation) => { - return ` - - ` -} +var editTemplate = require('../templates/includes/eventListings.pug'); +let viewTemplate = require('../templates/includes/viewEvents.pug'); module.exports = View.extend({ - template: template, bindings: { - 'model.name' : { - type: 'value', - hook: 'input-name-container' - }, 'model.selected' : { type: function (el, value, previousValue) { el.checked = value; }, hook: 'select' + }, + 'model.initialValue': { + hook: 'event-trigger-init-value', + type: 'booleanAttribute', + name: 'checked', + }, + 'model.persistent': { + hook: 'event-trigger-persistent', + type: 'booleanAttribute', + name: 'checked', } }, events: { @@ -72,8 +55,13 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode) { + this.delay = this.model.delay === "" ? "None" : this.model.delay; + } }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); @@ -82,7 +70,15 @@ module.exports = View.extend({ e.target.remove() }); if(!this.model.annotation){ - $(this.queryByHook('edit-annotation-btn')).text('Add') + $(this.queryByHook('edit-annotation-btn')).text('Add'); + } + if(this.viewMode) { + this.renderViewEventAssignments(); + if(this.model.useValuesFromTriggerTime) { + $(this.queryByHook("trigger-time")).prop('checked', true); + }else{ + $(this.queryByHook("assignment-time")).prop('checked', true); + } } }, update: function () { @@ -110,7 +106,7 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderEventListingsView(); + self.parent.renderEditEventListingsView(); modal.modal('hide'); }); }, @@ -118,6 +114,17 @@ module.exports = View.extend({ this.remove(); this.collection.removeEvent(this.model); }, + renderViewEventAssignments: function () { + if(this.viewEventAssignmentsView){ + this.viewEventAssignmentsView.remove() + } + this.viewEventAssignmentsView = new EventAssignment({ + collection: this.model.eventAssignments, + tooltips: this.parent.parent.tooltips, + readOnly: true + }); + app.registerRenderSubview(this, this.viewEventAssignmentsView, 'assignment-viewer'); + }, subviews: { inputName: { hook: 'event-name-container', diff --git a/client/views/events-editor.js b/client/views/events-editor.js index 73bfbcca24..e8dee5eeba 100644 --- a/client/views/events-editor.js +++ b/client/views/events-editor.js @@ -38,7 +38,6 @@ module.exports = View.extend({ initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.tooltips = Tooltips.eventsEditor - this.opened = attrs.opened this.collection.on("select", function (event) { this.setSelectedEvent(event); this.setDetailsView(event); @@ -59,7 +58,7 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEventListingsView(); + this.renderEditEventListingsView(); this.detailsContainer = this.queryByHook('event-details-container'); this.detailsViewSwitcher = new ViewSwitcher({ el: this.detailsContainer, @@ -69,22 +68,38 @@ module.exports = View.extend({ this.collection.trigger("select", this.selectedEvent); } this.toggleAddEventButton() - if(this.opened) { - this.openEventsContainer(); - } + this.renderViewEventListingView(); }, update: function () { }, updateValid: function () { }, - renderEventListingsView: function () { - if(this.eventListingsView){ - this.eventListingsView.remove(); + renderEditEventListingsView: function () { + if(this.editEventListingsView){ + this.editEventListingsView.remove(); } - this.eventListingsView = this.renderCollection( + this.editEventListingsView = this.renderCollection( this.collection, EventListings, - this.queryByHook('event-listing-container') + this.queryByHook('edit-event-listing-container') + ); + $(document).ready(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewEventListingView: function () { + if(this.viewEventListingsView) { + this.viewEventListingsView.remove(); + } + let options = {viewOptions: {viewMode: true}}; + this.viewEventListingsView = this.renderCollection( + this.collection, + EventListings, + this.queryByHook('view-event-listing-container'), + options ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); @@ -134,11 +149,6 @@ module.exports = View.extend({ this.parent.modelStateButtons.clickSaveHandler(e); this.parent.renderEventsView(mode="view"); }, - openEventsContainer: function () { - $(this.queryByHook('events')).collapse('show'); - let collapseBtn = $(this.queryByHook('collapse')) - collapseBtn.trigger('click') - }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, From 24c81e9b1365120c50d3b99ee6006d9d89721b04 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 13:17:54 -0400 Subject: [PATCH 057/133] Added an event listener to update the viewer when an event changes and fixed conflict with radio select. --- client/templates/includes/eventDetails.pug | 4 ++-- client/templates/includes/viewEvents.pug | 4 ++-- client/views/event-details.js | 8 ++++---- client/views/event-listings.js | 12 +++++++++--- client/views/events-editor.js | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/client/templates/includes/eventDetails.pug b/client/templates/includes/eventDetails.pug index f84bd82a05..2be091df93 100644 --- a/client/templates/includes/eventDetails.pug +++ b/client/templates/includes/eventDetails.pug @@ -63,10 +63,10 @@ div(data-hook="event-details") div.horizontal-space.inline - input(type="radio", name="use-values-from" data-hook="trigger-time" data-name="trigger") + input(type="radio", name="edit-use-values-from" data-hook="edit-trigger-time" data-name="trigger") | Trigger Time div.horizontal-space.inline - input(type="radio", name="use-values-from" data-hook="assignment-time" data-name="assignment") + input(type="radio", name="edit-use-values-from" data-hook="edit-assignment-time" data-name="assignment") | Assignment Time diff --git a/client/templates/includes/viewEvents.pug b/client/templates/includes/viewEvents.pug index 6f819a62e8..ef20893766 100644 --- a/client/templates/includes/viewEvents.pug +++ b/client/templates/includes/viewEvents.pug @@ -35,13 +35,13 @@ div.mx-1 div - input(type="radio", id="trigger-time", name="use-values-from" data-hook="trigger-time" disabled) + input(type="radio", id="trigger-time", name="view-use-values-from" data-hook="view-trigger-time" disabled) span.inline.horizontal-space(for="trigger-time") Trigger Time div - input(type="radio", id="assignment-time", name="use-values-from" data-hook="assignment-time" disabled) + input(type="radio", id="assignment-time", name="view-use-values-from" data-hook="view-assignment-time" disabled) span.inline.horizontal-space(for="assignment-time") Assignment Time diff --git a/client/views/event-details.js b/client/views/event-details.js index 0c35e2f8f1..0330f0030d 100644 --- a/client/views/event-details.js +++ b/client/views/event-details.js @@ -44,8 +44,8 @@ module.exports = View.extend({ events: { 'change [data-hook=event-trigger-init-value]' : 'setTriggerInitialValue', 'change [data-hook=event-trigger-persistent]' : 'setTriggerPersistent', - 'change [data-hook=trigger-time]' : 'setUseValuesFromTriggerTime', - 'change [data-hook=assignment-time]' : 'setUseValuesFromTriggerTime', + 'change [data-hook=edit-trigger-time]' : 'setUseValuesFromTriggerTime', + 'change [data-hook=edit-assignment-time]' : 'setUseValuesFromTriggerTime', 'click [data-hook=advanced-event-button]' : 'changeCollapseButtonText', }, initialize: function (attrs, options) { @@ -59,9 +59,9 @@ module.exports = View.extend({ var delayField = this.queryByHook('event-delay').children[0].children[1]; $(delayField).attr("placeholder", "---No Expression Entered---"); if(this.model.useValuesFromTriggerTime){ - $(this.queryByHook('trigger-time')).prop('checked', true) + $(this.queryByHook('edit-trigger-time')).prop('checked', true) }else{ - $(this.queryByHook('assignment-time')).prop('checked', true) + $(this.queryByHook('edit-assignment-time')).prop('checked', true) } $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); diff --git a/client/views/event-listings.js b/client/views/event-listings.js index 712e5a0c60..658828a165 100644 --- a/client/views/event-listings.js +++ b/client/views/event-listings.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ var $ = require('jquery'); +let _ = require('underscore'); //support files let app = require('../app'); var tests = require('./tests'); @@ -58,6 +59,8 @@ module.exports = View.extend({ this.viewMode = attrs.viewMode ? attrs.viewMode : false; if(this.viewMode) { this.delay = this.model.delay === "" ? "None" : this.model.delay; + }else{ + this.model.on("change", _.bind(this.updateViewer, this)); } }, render: function () { @@ -75,9 +78,9 @@ module.exports = View.extend({ if(this.viewMode) { this.renderViewEventAssignments(); if(this.model.useValuesFromTriggerTime) { - $(this.queryByHook("trigger-time")).prop('checked', true); + $(this.queryByHook("view-trigger-time")).prop('checked', true); }else{ - $(this.queryByHook("assignment-time")).prop('checked', true); + $(this.queryByHook("view-assignment-time")).prop('checked', true); } } }, @@ -120,11 +123,14 @@ module.exports = View.extend({ } this.viewEventAssignmentsView = new EventAssignment({ collection: this.model.eventAssignments, - tooltips: this.parent.parent.tooltips, + tooltips: this.parent.tooltips, readOnly: true }); app.registerRenderSubview(this, this.viewEventAssignmentsView, 'assignment-viewer'); }, + updateViewer: function () { + this.parent.renderViewEventListingView(); + }, subviews: { inputName: { hook: 'event-name-container', diff --git a/client/views/events-editor.js b/client/views/events-editor.js index e8dee5eeba..cb1321776b 100644 --- a/client/views/events-editor.js +++ b/client/views/events-editor.js @@ -94,7 +94,7 @@ module.exports = View.extend({ if(this.viewEventListingsView) { this.viewEventListingsView.remove(); } - let options = {viewOptions: {viewMode: true}}; + let options = {viewOptions: {parent: this, viewMode: true}}; this.viewEventListingsView = this.renderCollection( this.collection, EventListings, From b9aa5e64f433b1bbcea6f21f67ed17214ea95ddb Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 13:30:08 -0400 Subject: [PATCH 058/133] Added functions to trigger event viewer updates when an event assignment is changed. Fixed bug with setting the event assignment target. --- client/views/edit-event-assignment.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/views/edit-event-assignment.js b/client/views/edit-event-assignment.js index 01fdf954f9..8e48f4931f 100644 --- a/client/views/edit-event-assignment.js +++ b/client/views/edit-event-assignment.js @@ -31,6 +31,7 @@ module.exports = View.extend({ events: { 'click [data-hook=remove]' : 'removeAssignment', 'change [data-hook=event-assignment-variable]' : 'selectAssignmentVariable', + 'change [data-hook=event-assignment-expression]' : 'updateViewer' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); @@ -65,7 +66,7 @@ module.exports = View.extend({ selectAssignmentVariable: function (e) { var species = this.model.collection.parent.collection.parent.species; var parameters = this.model.collection.parent.collection.parent.parameters; - var val = e.target.selectedOptions.item(0).text; + var val = Number(e.target.value); var eventVar = species.filter(function (specie) { if(specie.compID === val) { return specie; @@ -79,8 +80,12 @@ module.exports = View.extend({ }); } this.model.variable = eventVar[0]; + this.updateViewer(); this.model.collection.parent.collection.trigger('change'); }, + updateViewer: function () { + this.model.collection.parent.trigger('change'); + }, subviews: { inputAssignmentExpression: { hook: 'event-assignment-expression', @@ -89,7 +94,7 @@ module.exports = View.extend({ parent: this, required: true, name: 'event-assignment-expression', - placehonder: "---No Expression Entered---", + placeholder: "---No Expression Entered---", modelKey: 'expression', valueType: 'string', value: this.model.expression, From b88e96ba45e805af7e84b30bc0ab88882f754aee Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 13:54:10 -0400 Subject: [PATCH 059/133] Setup the read only mode for the events editor. --- client/views/events-editor.js | 72 ++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/client/views/events-editor.js b/client/views/events-editor.js index cb1321776b..189c16fad5 100644 --- a/client/views/events-editor.js +++ b/client/views/events-editor.js @@ -37,37 +37,51 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.eventsEditor - this.collection.on("select", function (event) { - this.setSelectedEvent(event); - this.setDetailsView(event); - }, this); - this.collection.on("remove", function (event) { - // Select the last event by default - // But only if there are other events other than the one we're removing - if (event.detailsView) - event.detailsView.remove(); - this.collection.removeEvent(event); - if (this.collection.length) { - var selected = this.collection.at(this.collection.length-1); - this.collection.trigger("select", selected); - } - }, this); - this.collection.parent.species.on('add remove', this.toggleAddEventButton, this); - this.collection.parent.parameters.on('add remove', this.toggleAddEventButton, this); + if(!this.readOnly) { + this.collection.on("select", function (event) { + this.setSelectedEvent(event); + this.setDetailsView(event); + }, this); + this.collection.on("remove", function (event) { + // Select the last event by default + // But only if there are other events other than the one we're removing + if (event.detailsView) + event.detailsView.remove(); + this.collection.removeEvent(event); + if (this.collection.length) { + var selected = this.collection.at(this.collection.length-1); + this.collection.trigger("select", selected); + } + }, this); + this.collection.parent.species.on('add remove', this.toggleAddEventButton, this); + this.collection.parent.parameters.on('add remove', this.toggleAddEventButton, this); + } }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEditEventListingsView(); - this.detailsContainer = this.queryByHook('event-details-container'); - this.detailsViewSwitcher = new ViewSwitcher({ - el: this.detailsContainer, - }); - if (this.collection.length) { - this.setSelectedEvent(this.collection.at(0)); - this.collection.trigger("select", this.selectedEvent); + if(this.readOnly) { + $(this.queryByHook('events-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('events-view-tab')).tab('show'); + $(this.queryByHook('edit-events')).removeClass('active'); + $(this.queryByHook('view-events')).addClass('active'); + }else { + this.renderEditEventListingsView(); + this.detailsContainer = this.queryByHook('event-details-container'); + this.detailsViewSwitcher = new ViewSwitcher({ + el: this.detailsContainer, + }); + if(this.collection.length) { + this.setSelectedEvent(this.collection.at(0)); + this.collection.trigger("select", this.selectedEvent); + } + this.toggleAddEventButton() } - this.toggleAddEventButton() this.renderViewEventListingView(); }, update: function () { @@ -94,6 +108,12 @@ module.exports = View.extend({ if(this.viewEventListingsView) { this.viewEventListingsView.remove(); } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("events-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("events-annotation-header")).css("display", "block"); + } let options = {viewOptions: {parent: this, viewMode: true}}; this.viewEventListingsView = this.renderCollection( this.collection, From fdbec915a3bac1bef83c9fa2a527b5bfa6279f6f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 13:56:17 -0400 Subject: [PATCH 060/133] Refactored the model viewer to use the events editor in read only mode instead of the events viewer. --- client/views/model-viewer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index 4006bc62be..1500819b51 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -21,7 +21,7 @@ let app = require('../app'); //views let View = require('ampersand-view'); let RulesViewer = require('./rules-viewer'); -let EventsViewer = require('./events-viewer'); +let EventsViewer = require('./events-editor'); let SpeciesViewer = require('./species-viewer'); let ReactionsViewer = require('./reactions-viewer'); let ParametersEditor = require('./parameters-editor'); @@ -48,7 +48,8 @@ module.exports = View.extend({ }, renderEventsView: function () { let eventsViewer = new EventsViewer({ - collection: this.model.eventsCollection + collection: this.model.eventsCollection, + readOnly: true }); app.registerRenderSubview(this, eventsViewer, "events-viewer-container"); }, From 1c5f02bfb93f1191be74afa163efc3212a5b2cb5 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 21 Jun 2021 14:07:51 -0400 Subject: [PATCH 061/133] Removed dead files and cleaned up old code. --- client/pages/model-editor.js | 8 +-- client/templates/includes/eventsViewer.pug | 30 ----------- client/views/events-viewer.js | 54 -------------------- client/views/view-events.js | 58 ---------------------- 4 files changed, 1 insertion(+), 149 deletions(-) delete mode 100644 client/templates/includes/eventsViewer.pug delete mode 100644 client/views/events-viewer.js delete mode 100644 client/views/view-events.js diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index bdb85123e5..5fc1eeb80b 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -36,7 +36,6 @@ var ParticleViewer = require('../views/view-particle'); var ReactionsEditorView = require('../views/reactions-editor'); var ReactionsViewer = require('../views/reactions-viewer'); var EventsEditorView = require('../views/events-editor'); -var EventsViewer = require('../views/events-viewer'); var RulesEditorView = require('../views/rules-editor'); var RulesViewer = require('../views/rules-viewer'); var SBMLComponentView = require('../views/sbml-component-editor'); @@ -332,15 +331,10 @@ let ModelEditor = PageView.extend({ } app.registerRenderSubview(this, this.reactionsEditor, 'reactions-editor-container'); }, - renderEventsView: function (mode="edit", opened=false) { + renderEventsView: function () { if(this.eventsEditor){ this.eventsEditor.remove(); } - if(mode === "edit") { - this.eventsEditor = new EventsEditorView({collection: this.model.eventsCollection, opened: opened}); - }else{ - this.eventsEditor = new EventsViewer({collection: this.model.eventsCollection}); - } app.registerRenderSubview(this, this.eventsEditor, 'events-editor-container'); }, renderRulesView: function (mode="edit", opened=false) { diff --git a/client/templates/includes/eventsViewer.pug b/client/templates/includes/eventsViewer.pug deleted file mode 100644 index bf9bed0f61..0000000000 --- a/client/templates/includes/eventsViewer.pug +++ /dev/null @@ -1,30 +0,0 @@ -div#events-viewer.card.card-body - - div - - h3.inline Events - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-model-events-viewer", data-hook="collapse") - - - div.row.collapse(class="show", id="collapse-model-events-viewer") - - table.table - - thead - - tr - - th(scope="col") Name - - th(scope="col") Trigger - - th(scope="col") Assignments - - th(scope="col") Advanced - - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="view-events-container") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-events") Edit Events diff --git a/client/views/events-viewer.js b/client/views/events-viewer.js deleted file mode 100644 index fd5326b366..0000000000 --- a/client/views/events-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewEvent = require('./view-events'); -//templates -var template = require('../templates/includes/eventsViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-events]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewEvent, this.queryByHook('view-events-container')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderEventsView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/view-events.js b/client/views/view-events.js deleted file mode 100644 index 0b04864060..0000000000 --- a/client/views/view-events.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var AssignmentsViewer = require('./event-assignments-viewer'); -//templates -var template = require('../templates/includes/viewEvents.pug'); - -module.exports = View.extend({ - template: template, - bindings: { - 'model.initialValue': { - hook: 'event-trigger-init-value', - type: 'booleanAttribute', - name: 'checked', - }, - 'model.persistent': { - hook: 'event-trigger-persistent', - type: 'booleanAttribute', - name: 'checked', - } - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.delay = this.model.delay === "" ? "None" : this.model.delay - }, - render: function () { - View.prototype.render.apply(this, arguments); - var assignmentsViewer = new AssignmentsViewer({ - collection: this.model.eventAssignments - }); - app.registerRenderSubview(this, assignmentsViewer, 'assignment-viewer'); - if(this.model.useValuesFromTriggerTime) { - $(this.queryByHook("trigger-time")).prop('checked', true) - }else{ - $(this.queryByHook("assignment-time")).prop('checked', true) - } - } -}); \ No newline at end of file From 01e70eb89a30a04fcae8e655a61384bb81b37abd Mon Sep 17 00:00:00 2001 From: BryanRumsey <44621966+BryanRumsey@users.noreply.github.com> Date: Mon, 21 Jun 2021 14:55:03 -0400 Subject: [PATCH 062/133] Removed un-used variable --- client/pages/model-editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 20564efd26..e990ad01f9 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -467,7 +467,6 @@ let ModelEditor = PageView.extend({ } }, setDefaultMode: function (e) { - let self = this; let prevMode = this.model.defaultMode; this.model.defaultMode = e.target.dataset.name; this.speciesEditor.defaultMode = e.target.dataset.name; From 463007f316856b69030305590500cf1e2332992f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 08:07:40 -0400 Subject: [PATCH 063/133] Fixed the events render error. --- client/pages/model-editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 9f79c1cc3a..c5ca5808a4 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -342,6 +342,7 @@ let ModelEditor = PageView.extend({ if(this.eventsEditor){ this.eventsEditor.remove(); } + this.eventsEditor = new EventsEditorView({collection: this.model.eventsCollection}); app.registerRenderSubview(this, this.eventsEditor, 'events-editor-container'); }, renderRulesView: function (mode="edit", opened=false) { From dd627811c47e70c944dbbee197d524be2a47a012 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 08:14:24 -0400 Subject: [PATCH 064/133] Cleaned up the rules rneder function in the model editor. Added the readOnly attribute to the rules render function in the model viewer. --- client/pages/model-editor.js | 8 ++------ client/views/model-viewer.js | 5 +++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index e990ad01f9..8e89586ebf 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -350,15 +350,11 @@ let ModelEditor = PageView.extend({ } app.registerRenderSubview(this, this.eventsEditor, 'events-editor-container'); }, - renderRulesView: function (mode="edit", opened=false) { + renderRulesView: function () { if(this.rulesEditor){ this.rulesEditor.remove(); } - if(mode === "edit") { - this.rulesEditor = new RulesEditorView({collection: this.model.rules, opened: opened}); - }else{ - this.rulesEditor = new RulesViewer({collection: this.model.rules}) - } + this.rulesEditor = new RulesEditorView({collection: this.model.rules}); app.registerRenderSubview(this, this.rulesEditor, 'rules-editor-container'); }, renderSystemVolumeView: function () { diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index 25a7bdc11f..b0e9217446 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -20,7 +20,7 @@ along with this program. If not, see . let app = require('../app'); //views let View = require('ampersand-view'); -let RulesViewer = require('./rules-viewer'); +let RulesViewer = require('./rules-editor'); let EventsViewer = require('./events-viewer'); let SpeciesViewer = require('./species-editor'); let ReactionsViewer = require('./reactions-editor'); @@ -68,7 +68,8 @@ module.exports = View.extend({ }, renderRulesView: function () { let rulesViewer = new RulesViewer({ - collection: this.model.rules + collection: this.model.rules, + readOnly: true }); app.registerRenderSubview(this, rulesViewer, "rules-viewer-container"); }, From 5fddf3d9e313c4f40dde1db5f227f8cc8fdb2bf9 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 08:37:42 -0400 Subject: [PATCH 065/133] Re-designed the layout for the rules section to use a grid layout. --- client/templates/includes/editRule.pug | 25 ++++-- client/templates/includes/ruleEditor.pug | 106 ++++++++++++++--------- client/views/rules-editor.js | 2 +- 3 files changed, 81 insertions(+), 52 deletions(-) diff --git a/client/templates/includes/editRule.pug b/client/templates/includes/editRule.pug index 30c7547445..3d6460eaf4 100644 --- a/client/templates/includes/editRule.pug +++ b/client/templates/includes/editRule.pug @@ -1,17 +1,24 @@ -tr +div.mx-1 - td.name: div(data-hook="rule-name") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td: div(data-hook="rule-type") + div.row - td: div(data-hook="rule-variable") + div.col-sm-2(data-hook="rule-name") - td: div(data-hook="rule-expression") + div.col-sm-2(data-hook="rule-type") - td + div.col-sm-2(data-hook="rule-variable") - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-2(data-hook="rule-expression") - button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + div.col-sm-2 - td: button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X \ No newline at end of file + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit + + div.col-sm-2 + + button.btn.btn-outline-secondary.box-shadow(data-hook="remove") X diff --git a/client/templates/includes/ruleEditor.pug b/client/templates/includes/ruleEditor.pug index 7065291d4e..7f230e2d62 100644 --- a/client/templates/includes/ruleEditor.pug +++ b/client/templates/includes/ruleEditor.pug @@ -1,67 +1,89 @@ -div#rules-editor.card.card-body +div#rules-editor.card - div + div.card-header.pb-0 - h3.inline Rules + h3.inline.mr-3 Rules + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="rules-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="rules-edit-tab" data-toggle="tab" href="#edit-rules") Edit + + li.nav-item + + a.nav-link.tab(data-hook="rules-view-tab" data-toggle="tab" href="#view-rules") View button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-rules", data-hook="collapse") + - div.collapse(id="collapse-rules" data-hook="rules-list-container") - p + div.card-body + p.mb-0 | Equations that determine the value (assignment rule) or rates of change (rate rule) of Variables or Parameter. - table.table + div.collapse.tab-content(id="collapse-rules" data-hook="rules-list-container") + + div.tab-pane.active(id="edit-rules" data-hook="edit-rules") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-2 + + h6.inline Type + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.type) + + div.col-sm-2 - thead + h6.inline Target - tr + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) - th(scope="col") - div - div.inline Name + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + h6.inline Formula - th(scope="col") - div - div.inline Type + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.type) + div.col-sm-2 - th(scope="col") - div - div.inline Target + h6.inline Annotation - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - th(scope="col") - div - div.inline Formula + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + h6 Remove - th(scope="col") - div - div.inline Annotation + div.my-3(data-hook="edit-rule-list-container") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.dropdown - th(scope="col") Remove + button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addRuleBtn( + data-hook="add-rule", + data-toggle="dropdown", + aria-haspopup="true", + aria-expanded="false", + type="button" + ) Add Rule - tbody(data-hook="rule-list-container") + ul.dropdown-menu(aria-labelledby="addRuleBtn") + li.dropdown-item(data-hook="rate-rule" data-name="Rate Rule") Rate Rule + li.dropdown-item(data-hook="assignment-rule" data-name="Assignment Rule") Assignment Rule - div.dropdown.inline + div.tab-pane(id="view-rules" data-hook="view-rules") - button.btn.btn-outline-primary.dropdown-toggle.box-shadow#addRuleBtn( - data-hook="add-rule", - data-toggle="dropdown", - aria-haspopup="true", - aria-expanded="false", - type="button" - ) Add Rule + hr - ul.dropdown-menu(aria-labelledby="addRuleBtn") - li.dropdown-item(data-hook="rate-rule" data-name="Rate Rule") Rate Rule - li.dropdown-item(data-hook="assignment-rule" data-name="Assignment Rule") Assignment Rule + div.mx-1.row.head.align-items-baseline - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-rules") Save Rules + div.mt-3(data-hook="view-rules-list") diff --git a/client/views/rules-editor.js b/client/views/rules-editor.js index fdba4cb842..fc4903f318 100644 --- a/client/views/rules-editor.js +++ b/client/views/rules-editor.js @@ -60,7 +60,7 @@ module.exports = View.extend({ this.rulesView = this.renderCollection( this.collection, RuleView, - this.queryByHook('rule-list-container') + this.queryByHook('edit-rule-list-container') ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); From 12640f5a42fd5eeb5b6da625fefa369b76082e5d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 09:31:28 -0400 Subject: [PATCH 066/133] Cleaned up the subviews. Added the view template. Refactored the template to be set base on the value of the view mode attribute. --- client/views/edit-rule.js | 89 +++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/client/views/edit-rule.js b/client/views/edit-rule.js index 9c54a25471..2b9b734e2b 100644 --- a/client/views/edit-rule.js +++ b/client/views/edit-rule.js @@ -26,10 +26,10 @@ var View = require('ampersand-view'); var InputView = require('./input'); var SelectView = require('ampersand-select-view'); //templates -var template = require('../templates/includes/editRule.pug'); +var editTemplate = require('../templates/includes/editRule.pug'); +let viewTemplate = require('../templates/includes/viewRules.pug'); module.exports = View.extend({ - template: template, events: { 'change [data-hook=rule-type]' : 'selectRuleType', 'change [data-hook=rule-variable]' : 'selectRuleVariable', @@ -38,31 +38,11 @@ module.exports = View.extend({ }, initiailize: function (attrs, options) { View.prototype.initiailize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { - this.renderWithTemplate(); - var inputField = this.queryByHook('rule-expression').children[0].children[1]; - $(inputField).attr("placeholder", "---No Expression Entered---"); - var varOptions = this.getOptions(); - var typeOptions = ['Rate Rule', 'Assignment Rule'] - var typeSelectView = new SelectView({ - label: '', - name: 'type', - required: true, - idAttributes: 'cid', - options: typeOptions, - value: this.model.type, - }); - var variableSelectView = new SelectView({ - label: '', - name: 'variable', - required: true, - idAttributes: 'cid', - options: varOptions, - value: this.model.variable.name, - }); - app.registerRenderSubview(this, typeSelectView, "rule-type"); - app.registerRenderSubview(this, variableSelectView, 'rule-variable'); + this.template = this.viewMode ? viewTemplate : editTemplate; + View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); @@ -95,33 +75,38 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderRules(); + self.parent.renderEditRules(); modal.modal('hide'); }); }, getOptions: function () { var species = this.model.collection.parent.species; var parameters = this.model.collection.parent.parameters; - var speciesNames = species.map(function (specie) { return specie.name }); - var parameterNames = parameters.map(function (parameter) { return parameter.name }); - return speciesNames.concat(parameterNames); + var specs = species.map(function (specie) { + return [specie.compID, specie.name]; + }); + var params = parameters.map(function (parameter) { + return [parameter.compID, parameter.name]; + }); + let options = [{groupName: "Variables", options: specs}, + {groupName: "Parameters", options: params}]; + return options; }, selectRuleType: function (e) { - var type = e.target.selectedOptions.item(0).text; - this.model.type = type; + this.model.type = e.target.value; }, selectRuleVariable: function (e) { var species = this.model.collection.parent.species; var parameters = this.model.collection.parent.parameters; - var val = e.target.selectedOptions.item(0).text; + var compID = e.target.value; var ruleVar = species.filter(function (specie) { - if(specie.name === val) { + if(specie.compID === compID) { return specie; } }); if(!ruleVar.length) { ruleVar = parameters.filter(function (parameter) { - if(parameter.name === val) { + if(parameter.compID === compID) { return parameter; } }); @@ -145,7 +130,33 @@ module.exports = View.extend({ valueType: 'string', value: this.model.name, }); - }, + } + }, + selectType: { + hook: 'rule-type', + prepareView: function (el) { + let options = ['Rate Rule', 'Assignment Rule']; + return new SelectView({ + name: 'type', + required: true, + idAttributes: 'cid', + options: options, + value: this.model.type, + }); + } + }, + selectTarget: { + hook: 'rule-variable', + prepareView: function(el) { + let options = this.getOptions(); + return new SelectView({ + name: 'variable', + required: true, + idAttributes: 'cid', + groupOptions: options, + value: this.model.variable.compID, + }); + } }, inputRule: { hook: 'rule-expression', @@ -154,14 +165,12 @@ module.exports = View.extend({ parent: this, required: true, name: 'rule-expression', - label: '', - tests: '', modelKey: 'expression', valueType: 'string', value: this.model.expression, placeholder: "--No Formula Entered--" }); - }, - }, - }, + } + } + } }); \ No newline at end of file From e60803e4b900ebdeb69721ec1044921b02b88633 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 10:08:41 -0400 Subject: [PATCH 067/133] Merged the rules viewer into the rules editor. Fixed spelling error in the initialize function. --- client/templates/includes/editRule.pug | 16 ++++++-- client/templates/includes/ruleEditor.pug | 22 ++++++++++- client/views/edit-rule.js | 4 +- client/views/rules-editor.js | 49 +++++++++++++++--------- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/client/templates/includes/editRule.pug b/client/templates/includes/editRule.pug index 3d6460eaf4..cdbb34eaeb 100644 --- a/client/templates/includes/editRule.pug +++ b/client/templates/includes/editRule.pug @@ -5,13 +5,21 @@ div.mx-1 div.row - div.col-sm-2(data-hook="rule-name") + div.col-sm-2 + + div.pl-2(data-hook="rule-name") + + div.col-sm-2 - div.col-sm-2(data-hook="rule-type") + div(data-hook="rule-type") - div.col-sm-2(data-hook="rule-variable") + div.col-sm-2 + + div(data-hook="rule-variable") + + div.col-sm-2 - div.col-sm-2(data-hook="rule-expression") + div(data-hook="rule-expression") div.col-sm-2 diff --git a/client/templates/includes/ruleEditor.pug b/client/templates/includes/ruleEditor.pug index 7f230e2d62..97cfd2c051 100644 --- a/client/templates/includes/ruleEditor.pug +++ b/client/templates/includes/ruleEditor.pug @@ -86,4 +86,24 @@ div#rules-editor.card div.mx-1.row.head.align-items-baseline - div.mt-3(data-hook="view-rules-list") + div.col-sm-2 + + h6.inline Name + + div.col-sm-2 + + h6.inline Type + + div.col-sm-2 + + h6.inline Target + + div.col-sm-4 + + h6.inline Formula + + div.col-sm-2(data-hook="rules-annotation-header") + + h6.inline Annotation + + div.mt-3(data-hook="view-rules-list-container") diff --git a/client/views/edit-rule.js b/client/views/edit-rule.js index 2b9b734e2b..1344600484 100644 --- a/client/views/edit-rule.js +++ b/client/views/edit-rule.js @@ -36,8 +36,8 @@ module.exports = View.extend({ 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', 'click [data-hook=remove]' : 'removeRule', }, - initiailize: function (attrs, options) { - View.prototype.initiailize.apply(this, arguments); + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { diff --git a/client/views/rules-editor.js b/client/views/rules-editor.js index fc4903f318..954a1e1af2 100644 --- a/client/views/rules-editor.js +++ b/client/views/rules-editor.js @@ -31,7 +31,6 @@ module.exports = View.extend({ events: { 'click [data-hook=rate-rule]' : 'addRule', 'click [data-hook=assignment-rule]' : 'addRule', - 'click [data-hook=save-rules]' : 'switchToViewMode', 'click [data-hook=collapse]' : 'changeCollapseButtonText', }, initialize: function (args) { @@ -39,21 +38,18 @@ module.exports = View.extend({ this.collection.parent.species.on('add remove', this.toggleAddRuleButton, this); this.collection.parent.parameters.on('add remove', this.toggleAddRuleButton, this); this.tooltips = Tooltips.rulesEditor - this.opened = args.opened }, render: function () { View.prototype.render.apply(this, arguments); - this.renderRules(); - this.toggleAddRuleButton() - if(this.opened) { - this.openRulesContainer(); - } + this.renderEditRules(); + this.toggleAddRuleButton(); + this.renderViewRules(); }, update: function () { }, updateValid: function () { }, - renderRules: function () { + renderEditRules: function () { if(this.rulesView) { this.rulesView.remove(); } @@ -62,7 +58,31 @@ module.exports = View.extend({ RuleView, this.queryByHook('edit-rule-list-container') ); - $(document).ready(function () { + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewRules: function () { + if(this.viewRulesView) { + this.viewRulesView.remove(); + } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("rules-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("rules-annotation-header")).css("display", "block"); + } + let options = {viewOptions: {viewMode: true}}; + this.viewRulesView = this.renderCollection( + this.collection, + RuleView, + this.queryByHook('view-rules-list-container'), + options + ); + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -70,7 +90,7 @@ module.exports = View.extend({ }); }, toggleAddRuleButton: function () { - this.renderRules(); + // this.renderEditRules(); var numSpecies = this.collection.parent.species.length; var numParameters = this.collection.parent.parameters.length; var disabled = numSpecies <= 0 && numParameters <= 0 @@ -87,15 +107,6 @@ module.exports = View.extend({ }); }); }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderRulesView(mode="view"); - }, - openRulesContainer: function () { - $(this.queryByHook('rules-list-container')).collapse('show'); - let collapseBtn = $(this.queryByHook('collapse')) - collapseBtn.trigger('click') - }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, From 5568e11cae4d42cc22265b5d4c8fd06c0caee662 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 10:10:05 -0400 Subject: [PATCH 068/133] Re-designed the view rules template to use a grid layout. --- client/templates/includes/viewRules.pug | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/client/templates/includes/viewRules.pug b/client/templates/includes/viewRules.pug index f4b039523b..30bacfe4dd 100644 --- a/client/templates/includes/viewRules.pug +++ b/client/templates/includes/viewRules.pug @@ -1,14 +1,19 @@ -tr - td=this.model.name +div.mx-1 - td=this.model.type + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.model.variable.name + div.row - td=this.model.expression + div.col-sm-2=this.model.name - if this.model.annotation - td: div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-2=this.model.type - if this.parent.containsMdlWithAnn && !this.model.annotation - td: div + div.col-sm-2=this.model.variable.name + + div.col-sm-4=this.model.expression + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") From 8d420ca6c906a8e4762d22600da444bbf9d7a400 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 10:18:38 -0400 Subject: [PATCH 069/133] Added function to update the viewer whenever the rule is changed. --- client/views/edit-rule.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/views/edit-rule.js b/client/views/edit-rule.js index 1344600484..de9884510f 100644 --- a/client/views/edit-rule.js +++ b/client/views/edit-rule.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ var $ = require('jquery'); +let _ = require('underscore'); //support files let app = require('../app'); var tests = require('./tests'); @@ -43,6 +44,9 @@ module.exports = View.extend({ render: function () { this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)) + } $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); @@ -98,7 +102,7 @@ module.exports = View.extend({ selectRuleVariable: function (e) { var species = this.model.collection.parent.species; var parameters = this.model.collection.parent.parameters; - var compID = e.target.value; + var compID = Number(e.target.value); var ruleVar = species.filter(function (specie) { if(specie.compID === compID) { return specie; @@ -116,6 +120,9 @@ module.exports = View.extend({ removeRule: function () { this.model.collection.removeRule(this.model); }, + updateViewer: function () { + this.parent.renderViewRules(); + }, subviews: { inputName: { hook: 'rule-name', From af70f4febb0e160a22967c2a48e65feb7e1fd851 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 10:24:30 -0400 Subject: [PATCH 070/133] Added functions to support read only mode. --- client/views/rules-editor.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/client/views/rules-editor.js b/client/views/rules-editor.js index 954a1e1af2..1cd56c4a55 100644 --- a/client/views/rules-editor.js +++ b/client/views/rules-editor.js @@ -33,16 +33,28 @@ module.exports = View.extend({ 'click [data-hook=assignment-rule]' : 'addRule', 'click [data-hook=collapse]' : 'changeCollapseButtonText', }, - initialize: function (args) { + initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.collection.parent.species.on('add remove', this.toggleAddRuleButton, this); this.collection.parent.parameters.on('add remove', this.toggleAddRuleButton, this); this.tooltips = Tooltips.rulesEditor }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEditRules(); - this.toggleAddRuleButton(); + if(this.readOnly) { + $(this.queryByHook('rules-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('rules-view-tab')).tab('show'); + $(this.queryByHook('edit-rules')).removeClass('active'); + $(this.queryByHook('view-rules')).addClass('active'); + }else { + this.renderEditRules(); + this.toggleAddRuleButton(); + } this.renderViewRules(); }, update: function () { From d2f0686eadf6ac5cd51915364a0a822a8f7506e2 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 10:44:47 -0400 Subject: [PATCH 071/133] Removed dead files. --- client/templates/includes/rulesViewer.pug | 23 ---------- client/views/rules-viewer.js | 54 ----------------------- client/views/view-rules.js | 26 ----------- 3 files changed, 103 deletions(-) delete mode 100644 client/templates/includes/rulesViewer.pug delete mode 100644 client/views/rules-viewer.js delete mode 100644 client/views/view-rules.js diff --git a/client/templates/includes/rulesViewer.pug b/client/templates/includes/rulesViewer.pug deleted file mode 100644 index 1451df8482..0000000000 --- a/client/templates/includes/rulesViewer.pug +++ /dev/null @@ -1,23 +0,0 @@ -div#rules-viewer.card.card-body - - div - - h3.inline Rules - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-rules", data-hook="collapse") - - - div.collapse(class="show", id="collapse-rules") - - table.table - thead - tr - th.col-md-3-view(scope="col") Name - th.col-md-3-view(scope="col") Type - th.col-md-3-view(scope="col") Target - th.col-md-3-view(scope="col") Formula - if this.containsMdlWithAnn - th.col-md-3-view(scope="col") Annotation - - tbody(data-hook="rules-list") - - if this.collection.parent.for === "edit" - button.btn.btn-outline-primary.box-shadow(data-hook="edit-rules") Edit Rules diff --git a/client/views/rules-viewer.js b/client/views/rules-viewer.js deleted file mode 100644 index a8fce201da..0000000000 --- a/client/views/rules-viewer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -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 . -*/ - -var $ = require('jquery'); -//support files -let app = require('../app'); -//views -var View = require('ampersand-view'); -var ViewRules = require('./view-rules'); -//templates -var template = require('../templates/includes/rulesViewer.pug'); - -module.exports = View.extend({ - template: template, - events: { - 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-rules]' : 'switchToEditMode' - }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0 - }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderCollection(this.collection, ViewRules, this.queryByHook('rules-list')) - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip(); - $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); - }); - }, - switchToEditMode: function (e) { - this.parent.renderRulesView("edit", true); - }, - changeCollapseButtonText: function (e) { - app.changeCollapseButtonText(this, e); - }, -}); \ No newline at end of file diff --git a/client/views/view-rules.js b/client/views/view-rules.js deleted file mode 100644 index c4939f45d4..0000000000 --- a/client/views/view-rules.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -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 . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/viewRules.pug'); - -module.exports = View.extend({ - template: template, -}); \ No newline at end of file From 0c4e5e6f553a0a4c72c0b65e939e3c1016adfa68 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 10:51:01 -0400 Subject: [PATCH 072/133] Removed the rules viewer import. --- client/pages/model-editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 8e89586ebf..31b81fdf04 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -37,7 +37,6 @@ var ReactionsEditorView = require('../views/reactions-editor'); var EventsEditorView = require('../views/events-editor'); var EventsViewer = require('../views/events-viewer'); var RulesEditorView = require('../views/rules-editor'); -var RulesViewer = require('../views/rules-viewer'); var SBMLComponentView = require('../views/sbml-component-editor'); var TimespanSettingsView = require('../views/timespan-settings'); var ModelStateButtonsView = require('../views/model-state-buttons'); From 78eda9e1dce819e24e2ac69483e2a4c76db7ce79 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 11:06:47 -0400 Subject: [PATCH 073/133] Cleaned up the model render sbml components block in the model editor. Added the read only attribute to the sbml components block of the model viewer. --- client/pages/model-editor.js | 3 +-- client/views/model-viewer.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index e990ad01f9..534c25db5a 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -253,8 +253,7 @@ let ModelEditor = PageView.extend({ this.renderRulesView(); if(this.model.functionDefinitions.length) { var sbmlComponentView = new SBMLComponentView({ - functionDefinitions: this.model.functionDefinitions, - viewModel: false + functionDefinitions: this.model.functionDefinitions }); app.registerRenderSubview(this, sbmlComponentView, 'sbml-component-container'); } diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index 25a7bdc11f..ed15b4dc26 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -75,7 +75,7 @@ module.exports = View.extend({ renderSBMLComponentsView: function () { let sbmlComponentsView = new SBMLComponentsView({ functionDefinitions: this.model.functionDefinitions, - viewMode: true + readOnly: true }); app.registerRenderSubview(this, sbmlComponentsView, "sbml-components-viewer-container"); }, From 9efe8d04338f6f10edcf28cffd3f4e9e77fde05d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 11:17:11 -0400 Subject: [PATCH 074/133] Re-designed the layout for sbml components to use a grid layout. --- .../includes/sbmlComponentEditor.pug | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/client/templates/includes/sbmlComponentEditor.pug b/client/templates/includes/sbmlComponentEditor.pug index 7d4ec4423d..181567c17d 100644 --- a/client/templates/includes/sbmlComponentEditor.pug +++ b/client/templates/includes/sbmlComponentEditor.pug @@ -1,34 +1,37 @@ -div#sbml-components.card.card-body +div#sbml-components.card - div + div.card-header.pb-0 h3.inline SBML Components + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-sbml-component", data-hook="collapse") + div.collapse(id="collapse-sbml-component") - div + div.card-body + + div - h5.inline Function Definitions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-function-definitions", data-hook="collapse-function-definitions") - + h5.inline Function Definitions + button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-function-definitions", data-hook="collapse-function-definitions") - - div.collapse(class="show", id="collapse-function-definitions") + div.collapse(class="show", id="collapse-function-definitions") - table.table + table.table - thead - - tr - - th(scope="col") Signature + thead + + tr + + th(scope="col") Signature - th(scope="col") - div - div.inline Annotation + th(scope="col") + div + div.inline Annotation - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - if(!this.viewMode) - th(scope="col") Remove - - tbody(data-hook="function-definition-list") \ No newline at end of file + if(!this.viewMode) + th(scope="col") Remove + + tbody(data-hook="function-definition-list") \ No newline at end of file From 85669413b6c48651bbd2c8a67697cea3b9bd2b9e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 12:40:37 -0400 Subject: [PATCH 075/133] Re-designed the layout for function definitions to use a grid layout. --- .../includes/editFunctionDefinition.pug | 23 ++++--- .../includes/sbmlComponentEditor.pug | 62 ++++++++++++++----- client/views/edit-function-definition.js | 2 +- client/views/sbml-component-editor.js | 6 +- 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/client/templates/includes/editFunctionDefinition.pug b/client/templates/includes/editFunctionDefinition.pug index 1e9f7f5a54..74866ea363 100644 --- a/client/templates/includes/editFunctionDefinition.pug +++ b/client/templates/includes/editFunctionDefinition.pug @@ -1,13 +1,20 @@ -tr +div.mx-1 - td=this.model.signature + if(this.model.collection.indexOf(this.model) !== 0) + hr - th + div.row - div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + div.col-sm-8 + + div.pl-2=this.model.signature + + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") - if(!this.parent.viewMode) button.btn.btn-outline-secondary.btn-sm(data-hook="edit-annotation-btn") Edit - - if(!this.parent.viewMode) - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file + + div.col-sm-2 + + button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/sbmlComponentEditor.pug b/client/templates/includes/sbmlComponentEditor.pug index 181567c17d..280ecf7dc2 100644 --- a/client/templates/includes/sbmlComponentEditor.pug +++ b/client/templates/includes/sbmlComponentEditor.pug @@ -10,28 +10,56 @@ div#sbml-components.card div.card-body - div + div#function-definitions.card - h5.inline Function Definitions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-function-definitions", data-hook="collapse-function-definitions") - + div.card-header.pb-0 - div.collapse(class="show", id="collapse-function-definitions") + h5.inline.mr-3 Function Definitions - table.table + div.inline.mr-3 - thead - - tr - - th(scope="col") Signature + ul.nav.nav-tabs.card-header-tabs(id="function-definitions-tabs") - th(scope="col") - div - div.inline Annotation + li.nav-item + + a.nav-link.tab.active(data-hook="function-definitions-edit-tab" data-toggle="tab" href="#edit-function-definitions") Edit + + li.nav-item + + a.nav-link.tab(data-hook="function-definitions-view-tab" data-toggle="tab" href="#view-function-definitions") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-function-definitions" data-hook="collapse-function-definitions") - + + div.card-body + p.mb-0 + | Object representation defining an evaluable function to be used during simulation. + + div.collapse.tab-content(class="show" id="collapse-function-definitions") + + div.tab-pane.active(id="edit-function-definitions" data-hook="edit-function-definitions") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-8 + + h6 Signature + + div.col-sm-2 + + h6.inline Annotation div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - if(!this.viewMode) - th(scope="col") Remove - - tbody(data-hook="function-definition-list") \ No newline at end of file + div.col-sm-2 + + h6 Remove + + div.my-3(data-hook="function-definition-list") + + div.tab-pane(id="view-function-definitions" data-hook="view-function-definitions") + + hr + + div.mx-1.row.head.align-items-baseline diff --git a/client/views/edit-function-definition.js b/client/views/edit-function-definition.js index 61a3261b45..b97c5aa7b2 100644 --- a/client/views/edit-function-definition.js +++ b/client/views/edit-function-definition.js @@ -57,7 +57,7 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderEdirFunctionDefinitionView(); + self.parent.renderEditFunctionDefinitionView(); modal.modal('hide'); }); }, diff --git a/client/views/sbml-component-editor.js b/client/views/sbml-component-editor.js index 565b097bc4..e320295488 100644 --- a/client/views/sbml-component-editor.js +++ b/client/views/sbml-component-editor.js @@ -36,13 +36,13 @@ module.exports = View.extend({ View.prototype.initialize.apply(this, arguments); this.tooltips = Tooltips.sbmlComponentsEditor this.functionDefinitions = attrs.functionDefinitions; - this.viewMode = attrs.viewMode; + this.readOnly = attrs.readOnly ? attrs.readOnly : false; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEdirFunctionDefinitionView(); + this.renderEditFunctionDefinitionView(); }, - renderEdirFunctionDefinitionView: function () { + renderEditFunctionDefinitionView: function () { if(this.editFunctionDefinitionView){ this.editFunctionDefinitionView.remove(); } From 23b25496edba74d0c9b5eb5eb4688371ae900f2c Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 13:13:38 -0400 Subject: [PATCH 076/133] Merged the function definition viewer into the sbml components editor. Added a view function definition template. --- .../includes/sbmlComponentEditor.pug | 12 +++++++- .../includes/viewFunctionDefinition.pug | 15 ++++++++++ client/views/edit-function-definition.js | 6 ++-- client/views/sbml-component-editor.js | 29 +++++++++++++++++-- 4 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 client/templates/includes/viewFunctionDefinition.pug diff --git a/client/templates/includes/sbmlComponentEditor.pug b/client/templates/includes/sbmlComponentEditor.pug index 280ecf7dc2..84ac3d3fb5 100644 --- a/client/templates/includes/sbmlComponentEditor.pug +++ b/client/templates/includes/sbmlComponentEditor.pug @@ -56,10 +56,20 @@ div#sbml-components.card h6 Remove - div.my-3(data-hook="function-definition-list") + div.my-3(data-hook="edit-function-definition-list") div.tab-pane(id="view-function-definitions" data-hook="view-function-definitions") hr div.mx-1.row.head.align-items-baseline + + div.col-sm-10 + + h6 Signature + + div.col-sm-2(data-hook="function-definition-annotation-header") + + h6.inline Annotation + + div.my-3(data-hook="view-function-definition-list") diff --git a/client/templates/includes/viewFunctionDefinition.pug b/client/templates/includes/viewFunctionDefinition.pug new file mode 100644 index 0000000000..cdfc4ac78a --- /dev/null +++ b/client/templates/includes/viewFunctionDefinition.pug @@ -0,0 +1,15 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 0) + hr + + div.row + + div.col-sm-10 + + div.pl-2=this.model.signature + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") diff --git a/client/views/edit-function-definition.js b/client/views/edit-function-definition.js index b97c5aa7b2..bb7146befb 100644 --- a/client/views/edit-function-definition.js +++ b/client/views/edit-function-definition.js @@ -22,18 +22,20 @@ var modals = require('../modals'); //views var View = require('ampersand-view'); //templates -var template = require('../templates/includes/editFunctionDefinition.pug'); +var editTemplate = require('../templates/includes/editFunctionDefinition.pug'); +let viewTemplate = require('../templates/includes/viewFunctionDefinition.pug'); module.exports = View.extend({ - template: template, events: { 'click [data-hook=remove]' : 'removeFunctionDefinition', 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('hide.bs.modal', '.modal', function (e) { e.target.remove() diff --git a/client/views/sbml-component-editor.js b/client/views/sbml-component-editor.js index e320295488..5c4d0796dc 100644 --- a/client/views/sbml-component-editor.js +++ b/client/views/sbml-component-editor.js @@ -41,6 +41,7 @@ module.exports = View.extend({ render: function () { View.prototype.render.apply(this, arguments); this.renderEditFunctionDefinitionView(); + this.renderViewFunctionDefinitionView(); }, renderEditFunctionDefinitionView: function () { if(this.editFunctionDefinitionView){ @@ -49,9 +50,33 @@ module.exports = View.extend({ this.editFunctionDefinitionView = this.renderCollection( this.functionDefinitions, EditFunctionDefinition, - this.queryByHook('function-definition-list') + this.queryByHook('edit-function-definition-list') ); - $(document).ready(function () { + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewFunctionDefinitionView: function () { + if(this.viewFunctionDefinitionView) { + this.viewFunctionDefinitionView.remove(); + } + let options = {viewOptions: {viewMode: true}}; + this.containsMdlWithAnn = this.functionDefinitions.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("function-definition-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("function-definition-annotation-header")).css("display", "block"); + } + this.viewFunctionDefinitionView = this.renderCollection( + this.functionDefinitions, + EditFunctionDefinition, + this.queryByHook('view-function-definition-list'), + options + ); + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); From 9823337fbc462a1a6eaf8efe715be7038c11a294 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 13:18:17 -0400 Subject: [PATCH 077/133] Added function to support read only mode. --- client/views/sbml-component-editor.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/views/sbml-component-editor.js b/client/views/sbml-component-editor.js index 5c4d0796dc..e7f58940ab 100644 --- a/client/views/sbml-component-editor.js +++ b/client/views/sbml-component-editor.js @@ -40,7 +40,18 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEditFunctionDefinitionView(); + if(this.readOnly) { + $(this.queryByHook('function-definitions-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('function-definitions-view-tab')).tab('show'); + $(this.queryByHook('edit-function-definitions')).removeClass('active'); + $(this.queryByHook('view-function-definitions')).addClass('active'); + }else { + this.renderEditFunctionDefinitionView(); + } this.renderViewFunctionDefinitionView(); }, renderEditFunctionDefinitionView: function () { From ca9c5d3f377042d73f05491ce225fe9a3216aabe Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 14:08:41 -0400 Subject: [PATCH 078/133] Removed the species collection error message from the spatial species editor template. --- client/templates/includes/spatialSpeciesEditor.pug | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/templates/includes/spatialSpeciesEditor.pug b/client/templates/includes/spatialSpeciesEditor.pug index 472659cec4..ae24b9fc88 100644 --- a/client/templates/includes/spatialSpeciesEditor.pug +++ b/client/templates/includes/spatialSpeciesEditor.pug @@ -58,8 +58,6 @@ div#species-editor.card div.my-3(data-hook="edit-specie-list") - div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable - button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable div.tab-pane(id="view-species" data-hook="view-species") From 117a952e4c5a08545583cae945009a92e193be97 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 14:27:33 -0400 Subject: [PATCH 079/133] Added the component-valid class to the process component error message to hide it by default for spatial models and still be compatable with non-spatial models. --- client/templates/includes/reactionsEditor.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/templates/includes/reactionsEditor.pug b/client/templates/includes/reactionsEditor.pug index a05d6ee8b5..c3b6e5b7ef 100644 --- a/client/templates/includes/reactionsEditor.pug +++ b/client/templates/includes/reactionsEditor.pug @@ -61,7 +61,7 @@ div#reactions-editor.card div(data-hook="massaction-message"): p.text-info To add a mass action reaction the model must have at least one parameter - div(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule + div.component-valid(data-hook="process-component-error"): p.text-danger A model must have at least one reaction, event, or rule div.dropdown.inline From bae75f2e35e48581a0220b1dd13a6b7de2f7c95d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 14:28:50 -0400 Subject: [PATCH 080/133] Added checks that will only display component errors for non-spatial models. --- client/views/reactions-editor.js | 6 ++---- client/views/species-editor.js | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/views/reactions-editor.js b/client/views/reactions-editor.js index 96f30382a7..dc9b56ed77 100644 --- a/client/views/reactions-editor.js +++ b/client/views/reactions-editor.js @@ -237,11 +237,9 @@ module.exports = View.extend({ return ReactionTypes[type].label }, toggleProcessError: function () { - let errorMsg = $(this.queryByHook('process-component-error')) - if(this.collection.parent.is_spatial){ - errorMsg.html("

A model must have at least one reaction

") - } let model = this.collection.parent + if(model.is_spatial) {return}; + let errorMsg = $(this.queryByHook('process-component-error')) if(this.collection.length <= 0 && model.eventsCollection.length <= 0 && model.rules.length <= 0) { errorMsg.addClass('component-invalid') errorMsg.removeClass('component-valid') diff --git a/client/views/species-editor.js b/client/views/species-editor.js index 8f9824ee20..6eb3a6b445 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -178,6 +178,7 @@ module.exports = View.extend({ }); }, toggleSpeciesCollectionError: function () { + if(this.spatial) {return}; let errorMsg = $(this.queryByHook('species-collection-error')) if(this.collection.length <= 0) { errorMsg.addClass('component-invalid') From 960db4814477792d65fe97f27abb5b46552c8366 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 14:36:41 -0400 Subject: [PATCH 081/133] Added checks to the model validation function that will only check collection lengths if the model is not spatial. --- client/models/model.js | 4 ++-- client/models/species.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/models/model.js b/client/models/model.js index b8b59ab441..139c9647a5 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -91,12 +91,12 @@ module.exports = Model.extend({ this.rules.on('add change remove', this.updateValid, this); }, validateModel: function () { - if(!this.species.validateCollection()) return false; + if(!this.species.validateCollection(this.is_spatial)) return false; if(!this.parameters.validateCollection()) return false; if(!this.reactions.validateCollection()) return false; if(!this.eventsCollection.validateCollection()) return false; if(!this.rules.validateCollection()) return false; - if(this.reactions.length <= 0 && this.eventsCollection.length <= 0 && this.rules.length <= 0) { + if(!this.is_spatial && this.reactions.length <= 0 && this.eventsCollection.length <= 0 && this.rules.length <= 0) { this.error = {"type":"process"} return false; } diff --git a/client/models/species.js b/client/models/species.js index 5ba9377b71..b9ee6dc508 100644 --- a/client/models/species.js +++ b/client/models/species.js @@ -55,8 +55,8 @@ module.exports = Collection.extend({ this.remove(specie); this.parent.updateValid() }, - validateCollection: function () { - if(this.length <= 0) { + validateCollection: function (isSpatial) { + if(this.length <= 0 && !isSpatial) { this.parent.error = {'type':'species'} return false; } From 57f612aa6f40dc08dce61cfd906011a0383d443b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 15:02:20 -0400 Subject: [PATCH 082/133] Resovled review comments. --- client/views/rules-editor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/views/rules-editor.js b/client/views/rules-editor.js index 1cd56c4a55..6c0145acad 100644 --- a/client/views/rules-editor.js +++ b/client/views/rules-editor.js @@ -102,7 +102,7 @@ module.exports = View.extend({ }); }, toggleAddRuleButton: function () { - // this.renderEditRules(); + this.renderEditRules(); var numSpecies = this.collection.parent.species.length; var numParameters = this.collection.parent.parameters.length; var disabled = numSpecies <= 0 && numParameters <= 0 @@ -111,7 +111,7 @@ module.exports = View.extend({ addRule: function (e) { var type = e.target.dataset.name this.collection.addRule(type); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); From d4afa33d6ac8f8cbf2ac8a687924b82beb196117 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 15:50:19 -0400 Subject: [PATCH 083/133] Added properties to the spatial model preview target selection modal. --- client/modals.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/client/modals.js b/client/modals.js index c55db1f6b8..db5185cc64 100644 --- a/client/modals.js +++ b/client/modals.js @@ -424,15 +424,26 @@ module.exports = { return templates.select(modalID, selectID, title, label, options) }, - selectSpeciesHTML : (species) => { - let modalID = "speciesSelectModal"; - let selectID = "speciesSelectList"; - let title = "Preview Variable Selection"; - let label = "Select a variable to preview: "; + selectPreviewTargetHTML : (species) => { + let modalID = "previewTargetSelectModal"; + let selectID = "previewTargetSelectList"; + let title = "Preview Target Selection"; + let label = "Select a variable or property to preview: "; var options = species.map(function (name) { return `` }); - options = options.join(" "); + options = ` + ${options.join(" ")} + + + + + + + + + + `; return templates.select(modalID, selectID, title, label, options) }, From 87c2484f0accb81b3410b046fd3f01ffb887549b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 15:51:37 -0400 Subject: [PATCH 084/133] Refactored the spatial model target selection process function to reflect that users can also choose species. --- client/views/model-state-buttons.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client/views/model-state-buttons.js b/client/views/model-state-buttons.js index f7d5bebf8a..ff24e5df62 100644 --- a/client/views/model-state-buttons.js +++ b/client/views/model-state-buttons.js @@ -63,7 +63,7 @@ module.exports = View.extend({ Plotly.purge(el) $(this.parent.queryByHook('preview-plot-buttons')).css("display", "none"); if(this.model.is_spatial) { - this.saveModel(this.getPreviewSpecies.bind(this)); + this.saveModel(this.getPreviewTarget.bind(this)); }else{ this.saveModel(this.runModel.bind(this)); } @@ -94,19 +94,19 @@ module.exports = View.extend({ window.location.href = endpoint }) }, - getPreviewSpecies: function () { + getPreviewTarget: function () { this.saved(); let species = this.model.species.map(function (species) { return species.name }); let self = this; - let modal = $(modals.selectSpeciesHTML(species)).modal(); - let okBtn = document.querySelector("#speciesSelectModal .ok-model-btn"); - let select = document.querySelector("#speciesSelectModal #speciesSelectList"); + let modal = $(modals.selectPreviewTargetHTML(species)).modal(); + let okBtn = document.querySelector("#previewTargetSelectModal .ok-model-btn"); + let select = document.querySelector("#previewTargetSelectModal #previewTargetSelectList"); okBtn.addEventListener('click', function (e) { modal.modal('hide'); - let specie = select.value; - self.runModel(specie); + let target = select.value; + self.runModel(target); }); }, togglePreviewWorkflowBtn: function () { @@ -149,8 +149,8 @@ module.exports = View.extend({ saved.style.display = "none"; }, 5000); }, - runModel: function (species=null) { - if(typeof species !== "string") { + runModel: function (target=null) { + if(typeof target !== "string") { this.saved(); } this.running(); @@ -158,7 +158,7 @@ module.exports = View.extend({ var model = this.model var queryStr = "?cmd=start&outfile=none&path="+model.directory if(species) { - queryStr += "&species=" + species; + queryStr += "&target=" + target; } var endpoint = path.join(app.getApiPath(), 'model/run')+queryStr; var self = this; From 09265fee00375944aebd4bc781e37284893d86e2 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 16:44:26 -0400 Subject: [PATCH 085/133] renamed species to target for spatial model preview target selections. Refactored the preview ploting to generate either a properties plot or species plot depending on the target. --- client/modals.js | 6 ++--- client/views/model-state-buttons.js | 2 +- stochss/handlers/models.py | 8 +++--- stochss/handlers/util/scripts/run_preview.py | 4 +-- stochss/handlers/util/spatial_simulation.py | 28 +++++++++++++------- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/client/modals.js b/client/modals.js index db5185cc64..54b3aaadf2 100644 --- a/client/modals.js +++ b/client/modals.js @@ -437,9 +437,9 @@ module.exports = { - - - + + + diff --git a/client/views/model-state-buttons.js b/client/views/model-state-buttons.js index ff24e5df62..4683565571 100644 --- a/client/views/model-state-buttons.js +++ b/client/views/model-state-buttons.js @@ -157,7 +157,7 @@ module.exports = View.extend({ $(this.parent.queryByHook('model-run-container')).css("display", "block") var model = this.model var queryStr = "?cmd=start&outfile=none&path="+model.directory - if(species) { + if(target) { queryStr += "&target=" + target; } var endpoint = path.join(app.getApiPath(), 'model/run')+queryStr; diff --git a/stochss/handlers/models.py b/stochss/handlers/models.py index b4a4a62755..36d3a4e68e 100644 --- a/stochss/handlers/models.py +++ b/stochss/handlers/models.py @@ -209,14 +209,14 @@ async def get(self): if outfile == 'none': outfile = str(uuid.uuid4()).replace("-", "_") log.debug("Temporary outfile: %s", outfile) - species = self.get_query_argument(name="species", default=None) + target = self.get_query_argument(name="target", default=None) resp = {"Running":False, "Outfile":outfile, "Results":""} if run_cmd == "start": exec_cmd = ['/stochss/stochss/handlers/util/scripts/run_preview.py', f'{path}', f'{outfile}'] - if species is not None: - exec_cmd.insert(1, "--species") - exec_cmd.insert(2, f"{species}") + if target is not None: + exec_cmd.insert(1, "--target") + exec_cmd.insert(2, f"{target}") log.debug("Script commands for running a preview: %s", exec_cmd) subprocess.Popen(exec_cmd) resp['Running'] = True diff --git a/stochss/handlers/util/scripts/run_preview.py b/stochss/handlers/util/scripts/run_preview.py index 013f2b3d91..89cd9c6c1f 100755 --- a/stochss/handlers/util/scripts/run_preview.py +++ b/stochss/handlers/util/scripts/run_preview.py @@ -75,7 +75,7 @@ def get_parsed_args(): parser = argparse.ArgumentParser(description=description) parser.add_argument('path', help="The path from the user directory to the model.") parser.add_argument('outfile', help="The temp file used to hold the results.") - parser.add_argument('--species', help="Spatial species to preview.", default=None) + parser.add_argument('--target', help="Spatial species or property to preview.", default=None) return parser.parse_args() @@ -107,7 +107,7 @@ def run_preview(job): is_spatial = args.path.endswith(".smdl") if is_spatial: model = StochSSSpatialModel(path=args.path) - wkfl = SpatialSimulation(path="", preview=True, species=args.species) + wkfl = SpatialSimulation(path="", preview=True, target=args.target) wkfl.s_py_model = model.convert_to_spatialpy() wkfl.s_model = model.model else: diff --git a/stochss/handlers/util/spatial_simulation.py b/stochss/handlers/util/spatial_simulation.py index c2fd231b00..955af08715 100644 --- a/stochss/handlers/util/spatial_simulation.py +++ b/stochss/handlers/util/spatial_simulation.py @@ -30,7 +30,7 @@ class SpatialSimulation(StochSSJob): TYPE = "spatial" - def __init__(self, path, preview=False, species=None): + def __init__(self, path, preview=False, target=None): ''' Intitialize a spatial ensemble simulation job object @@ -38,13 +38,17 @@ def __init__(self, path, preview=False, species=None): ---------- path : str Path to the spatial ensemble simulation job + preview : bool + Indicates whether or not the simulation is a preview. + target : string + Results data target used for ploting ''' super().__init__(path=path) if not preview: self.settings = self.load_settings() self.s_py_model, self.s_model = self.load_models() else: - self.species = species + self.target = target def __get_run_settings(self): @@ -67,14 +71,20 @@ def run(self, preview=False, verbose=False): if verbose: self.log("info", "Running a preview spatial ensemble simulation") results = self.s_py_model.run(timeout=60) - # if self.species is None: - # self.species = list(self.s_py_model.get_all_species().keys())[0] + properties = ["type", "rho", "mass", "nu"] t_ndx_list = list(range(len(os.listdir(results.result_dir)) - 1)) - plot = results.plot_species(species=self.species, t_ndx_list=t_ndx_list, animated=True, - concentration=self.s_model['defaultMode'] == "continuous", - deterministic=self.s_model['defaultMode'] == "discrete", - width=None, height=None, return_plotly_figure=True, - f_duration=100, t_duration=100) + kwargs = {"t_ndx_list": t_ndx_list, "animated": True, "width": None, "height": None, + "return_plotly_figure": True, "f_duration": 100, "t_duration": 100} + if self.target in properties or self.target.startswith("v["): + if self.target.startswith("v["): + kwargs['p_ndx'] = int(self.target[2]) + self.target = "v" + plot = results.plot_property(property_name=self.target, **kwargs) + else: + concentration = self.s_model['defaultMode'] == "continuous" + deterministic = self.s_model['defaultMode'] == "discrete" + plot = results.plot_species(species=self.target, concentration=concentration, + deterministic=deterministic, **kwargs) plot["layout"]["autosize"] = True plot["config"] = {"responsive": True, "displayModeBar": True} return plot From da3adf22351f671f1f0e0e1916c291164e3d43b1 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 17:02:43 -0400 Subject: [PATCH 086/133] Minor code cleanup. Fixed placeholder for delay and trigger expression. --- client/views/event-details.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/client/views/event-details.js b/client/views/event-details.js index 0330f0030d..b0cc2aca09 100644 --- a/client/views/event-details.js +++ b/client/views/event-details.js @@ -54,16 +54,12 @@ module.exports = View.extend({ render: function () { View.prototype.render.apply(this, arguments); this.renderEventAssignments(); - var triggerExpressionField = this.queryByHook('event-trigger-expression').children[0].children[1]; - $(triggerExpressionField).attr("placeholder", "---No Expression Entered---"); - var delayField = this.queryByHook('event-delay').children[0].children[1]; - $(delayField).attr("placeholder", "---No Expression Entered---"); if(this.model.useValuesFromTriggerTime){ $(this.queryByHook('edit-trigger-time')).prop('checked', true) }else{ $(this.queryByHook('edit-assignment-time')).prop('checked', true) } - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -112,8 +108,7 @@ module.exports = View.extend({ parent: this, required: false, name: 'delay', - label: '', - tests: '', + placeholder: '---No Expression Entered---', modelKey: 'delay', valueType: 'string', value: this.model.delay, @@ -127,8 +122,6 @@ module.exports = View.extend({ parent: this, required: true, name: 'priority', - label: '', - tests: '', modelKey: 'priority', valueType: 'string', value: this.model.priority, @@ -142,8 +135,7 @@ module.exports = View.extend({ parent: this, required: true, name: 'trigger-expression', - label: '', - tests: '', + placeholder: '---No Expression Entered---', modelKey: 'triggerExpression', valueType: 'string', value: this.model.triggerExpression, From 77cdc00bbed4f6766b11152d2da8f07a0ec74434 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 17:04:00 -0400 Subject: [PATCH 087/133] Fixed type in priority label. Fixed styling for trigger expression. --- client/templates/includes/eventDetails.pug | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/templates/includes/eventDetails.pug b/client/templates/includes/eventDetails.pug index 2be091df93..c473f51381 100644 --- a/client/templates/includes/eventDetails.pug +++ b/client/templates/includes/eventDetails.pug @@ -1,12 +1,14 @@ div(data-hook="event-details") - div + div.row - span(for="event-trigger-expression") Trigger Expression: + div + + span(for="event-trigger-expression") Trigger Expression: div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.triggerExpression) - div.col-md-8.inline(id="event-trigger-expression" data-hook="event-trigger-expression") + div.col-md-8(id="event-trigger-expression" data-hook="event-trigger-expression") div(data-hook="event-assignments") @@ -29,7 +31,7 @@ div(data-hook="event-details") div.col-md-6 - span(for="event-trigger-expression") Prioirty: + span(for="event-trigger-expression") Priority: div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.tooltips.priority) From f30cf185695b2b6f8f1206c5549c52fa20504a5b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 22 Jun 2021 17:07:47 -0400 Subject: [PATCH 088/133] Resolved review comments. --- .../includes/eventAssignmentsEditor.pug | 2 +- client/views/event-assignments-editor.js | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/client/templates/includes/eventAssignmentsEditor.pug b/client/templates/includes/eventAssignmentsEditor.pug index ea9fceb423..f32e4cf6da 100644 --- a/client/templates/includes/eventAssignmentsEditor.pug +++ b/client/templates/includes/eventAssignmentsEditor.pug @@ -10,7 +10,7 @@ div#event-assignments-editor div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.assignments) - button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-event-assignments" data-hook="collapse") - + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#collapse-event-assignments" data-hook="collapse-assignments") - div diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index 41769b0d3a..7f790869f5 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -17,6 +17,8 @@ along with this program. If not, see . */ let $ = require('jquery'); +//support files +let app = require('../app'); //views var View = require('ampersand-view'); var EditEventAssignment = require('./edit-event-assignment'); @@ -26,7 +28,8 @@ var template = require('../templates/includes/eventAssignmentsEditor.pug'); module.exports = View.extend({ template: template, events: { - 'click [data-hook=add-event-assignment]' : 'addAssignment', + 'click [data-hook=collapse-assignments]' : 'changeCollapseButtonText', + 'click [data-hook=add-event-assignment]' : 'addAssignment' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); @@ -44,6 +47,13 @@ module.exports = View.extend({ this.renderEditEventAssignment(); } }, + addAssignment: function () { + this.collection.addEventAssignment(); + this.collection.parent.collection.trigger('change') + }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, renderEditEventAssignment: function () { if(this.editEventAssignments) { this.editEventAssignments.remove(); @@ -69,9 +79,5 @@ module.exports = View.extend({ update: function () { }, updateValid: function () { - }, - addAssignment: function () { - this.collection.addEventAssignment(); - this.collection.parent.collection.trigger('change') - }, + } }) \ No newline at end of file From 7a9a69c78e4c5a0ba2bb923fb1c14b268824c401 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 14:08:18 -0400 Subject: [PATCH 089/133] Re-designed the layout for the domain viewer. --- client/templates/includes/domainViewer.pug | 189 ++++++++++++--------- 1 file changed, 111 insertions(+), 78 deletions(-) diff --git a/client/templates/includes/domainViewer.pug b/client/templates/includes/domainViewer.pug index 296b7a0d7f..cbfe1f2478 100644 --- a/client/templates/includes/domainViewer.pug +++ b/client/templates/includes/domainViewer.pug @@ -1,113 +1,146 @@ -div#domain-viewer.card.card-body +div#domain-viewer.card - div + div.card-header.pb-0 - h3.inline Domain + h3.inline.mr-3 Domain + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="domain-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="domain-edit-tab" data-toggle="tab" href="#edit-domain") Edit + + li.nav-item + + a.nav-link.tab(data-hook="domain-view-tab" data-toggle="tab" href="#view-domain") View button.btn.btn-outline-collapse(id="-btn" data-toggle="collapse" data-target="#collapse-domain" data-hook="collapse") + div.collapse(id="collapse-domain" data-hook="domain-container") - div + div.card-body.tab-content + + div(data-hook="external-domains-container") + + button.btn.btn-outline-secondary.box-shadow.mb-3(data-hook="select-external-domain") View External Domain + + div(data-hook="external-domain-select" style="display: none") + + div.text-info(data-hook="select-location-message" style="display: none") + | There are multiple domain files with that name, please select a location + + span.inline Select External Domain: + + div.inline(data-hook="select-domain") + + div.inline.ml-3(data-hook="select-domain-location" style="display: none") + + span.inline Location: + + div.inline(data-hook="select-location") + + div.row + + div.col-md-4 + + h4 Domain Properties + + hr.mt-2 + div.row + div.col-sm-6: h6.pl-2 Static Domain + div.col-sm-6: input(type="checkbox" id="static-domain" data-hook="static-domain" checked=this.model.static disabled) + + hr.mt-2 + div.row + div.col-sm-6: h6.pl-2 Density + div.col-sm-6=this.model.rho_0 + + hr.mt-2 + div.row + div.col-sm-6: h6.pl-2 Gravity + div.col-sm-6=this.gravity + + hr.mt-2 + div.row + div.col-sm-6: h6.pl-2 Pressure + div.col-sm-6=this.model.p_0 - button.btn.btn-outline-secondary.box-shadow.mb-3(data-hook="select-external-domain") View External Domain + hr.mt-2 + div.row + div.col-sm-6: h6.pl-2 Speed of Sound + div.col-sm-6=this.model.c_0 - div(data-hook="external-domain-select" style="display: none") + div.col-md-8 - div.text-info(data-hook="select-location-message" style="display: none") - | There are multiple domain files with that name, please select a location + h4 Domain Limits - span.inline Select External Domain: + hr.mt-2 + div.row.head.mx-1 + div.col-sm-3 + div.col-sm-3.pb-1: h6 Minimum + div.col-sm-3: h6 Minimum + div.col-sm-3: h6 Reflect - div.inline(data-hook="select-domain") + div.row.mt-3 + div.col-sm-3: h6.pl-2 X-Axis + div.col-sm-3=this.model.x_lim[0] + div.col-sm-3=this.model.x_lim[1] + div.col-sm-3: input(type="checkbox" checked=this.model.boundary_condition.reflect_x disabled) - div.inline.ml-3(data-hook="select-domain-location" style="display: none") + hr.mt-2 + div.row + div.col-sm-3: h6.pl-2 Y-Axis + div.col-sm-3=this.model.y_lim[0] + div.col-sm-3=this.model.y_lim[1] + div.col-sm-3: input(type="checkbox" checked=this.model.boundary_condition.reflect_y disabled) - span.inline Location: + hr.mt-2 + div.row + div.col-sm-3: h6.pl-2 Z-Axis + div.col-sm-3=this.model.z_lim[0] + div.col-sm-3=this.model.z_lim[1] + div.col-sm-3: input(type="checkbox" checked=this.model.boundary_condition.reflect_z disabled) - div.inline(data-hook="select-location") + div.mt-3 - div.row + h4 Types - div.col-md-4 + hr - h4 Domain Properties + h6="Number of Un-Assigned Particles: " + this.model.types.get(0, "typeID").numParticles - table.table - tbody - tr - th(scope="row") Static Domain - td: input(type="checkbox" id="static-domain" data-hook="static-domain" checked=this.model.static disabled) - tr - th(scope="row") Density - td=this.model.rho_0 - tr - th(scope="row") Gravity - td=this.gravity - tr - th(scope="row") Pressure - td=this.model.p_0 - tr - th(scope="row") Speed of Sound - td=this.model.c_0 + div(data-hook="domain-error"): p.text-danger A domain cannot have any un-assigned particles. - div.col-md-8 + hr - h4 Domain Limits + div.mx-1.row.head.align-items-baseline - table.table - thead - tr - th(scope="col") - th(scope="col") Minimum - th(scope="col") Minimum - th(scope="col") Reflect + div.col-sm-2: h6.pl-2 Name - tbody - tr - th(scope="row") X-Axis - td=this.model.x_lim[0] - td=this.model.x_lim[1] - td: input(type="checkbox" checked=this.model.boundary_condition.reflect_x disabled) - tr - th(scope="row") Y-Axis - td=this.model.y_lim[0] - td=this.model.y_lim[1] - td: input(type="checkbox" checked=this.model.boundary_condition.reflect_y disabled) - tr - th(scope="row") Z-Axis - td=this.model.z_lim[0] - td=this.model.z_lim[1] - td: input(type="checkbox" checked=this.model.boundary_condition.reflect_z disabled) + div.col-sm-2: h6 Number of Particles - div + div.col-sm-2: h6 Mass - h4 Types + div.col-sm-2: h6 Volume - hr + div.col-sm-2: h6 Viscosity - h6="Number of Un-Assigned Particles: " + this.model.types.get(0, "typeID").numParticles + div.col-sm-2: h6 Fixed - div(data-hook="domain-error"): p.text-danger A domain cannot have any un-assigned particles. + div(data-hook="domain-types-list") - table.table - thead - tr - th(scope="col") Name - th(scope="col") Number of Particles - th(scope="col") Mass - th(scope="col") Volume - th(scope="col") Viscosity - th(scope="col") Fixed + div.tab-pane.active(id="edit-domain" data-hook="edit-domain") - tbody(data-hook="domain-types-list") + hr - hr + div - div + button.btn.btn-outline-primary.box-shadow(data-hook="edit-domain") Edit Domain - button.btn.btn-outline-primary.box-shadow(data-hook="edit-domain") Edit Domain + button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="create-domain") Build New Domain - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="create-domain") Build New Domain + button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-to-model" disabled) Import into Model - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-to-model" disabled) Import into Model + div.tab-pane(id="view-domain" data-hook="view-domain") From 44bbd3f4bea9d2d5fedff65e59533437b1f78ce8 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 14:36:21 -0400 Subject: [PATCH 090/133] Cleaned up the name input subview. Added option viewMode property. Added template selection based on view mode. --- client/views/edit-domain-type.js | 43 ++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/client/views/edit-domain-type.js b/client/views/edit-domain-type.js index 20bba307e3..bd3b028c17 100644 --- a/client/views/edit-domain-type.js +++ b/client/views/edit-domain-type.js @@ -22,10 +22,10 @@ let app = require('../app'); var View = require('ampersand-view'); var InputView = require('./input'); //templates -var template = require('../templates/includes/editDomainType.pug'); +let editTemplate = require('../templates/includes/editDomainType.pug'); +let viewTemplate = require('../templates/includes/viewDomainType.pug'); module.exports = View.extend({ - template: template, events: { 'click [data-hook=unassign-all]' : 'handleUnassignParticles', 'click [data-hook=delete-type]' : 'handleDeleteType', @@ -33,6 +33,15 @@ module.exports = View.extend({ 'click [data-hook=edit-defaults-btn]' : 'handleEditDefaults', 'change [data-target=type-name]' : 'handleRenameType' }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + console.log(this.model.typeID) + }, + render: function (attrs, options) { + this.template = this.viewMode ? viewTemplate : editTemplate; + View.prototype.render.apply(this, arguments); + }, handleDeleteType: function (e) { let type = Number(e.target.dataset.type); this.parent.deleteType(type); @@ -58,19 +67,21 @@ module.exports = View.extend({ this.parent.unassignAllParticles(type); this.parent.renderDomainTypes(); }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - }, - render: function (attrs, options) { - View.prototype.render.apply(this, arguments); - this.renderNameView(); - }, - renderNameView: function () { - let nameView = new InputView({parent: this, required: true, - name: 'name', valueType: 'string', - value: this.model.name}); - app.registerRenderSubview(this, nameView, "type-" + this.model.typeID); - }, update: function () {}, - updateValid: function () {} + updateValid: function () {}, + subviews: { + inputName: { + waitFor: "model.typeID", + prepareView: function (el) { + return new InputView({ + el: "type-" + this.model.typeID, + parent: this, + required: true, + name: 'name', + valueType: 'string', + value: this.model.name + }); + } + } + } }); \ No newline at end of file From a31b56d839871230644aae9b9c59862b503d962e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 14:37:36 -0400 Subject: [PATCH 091/133] Refactored the domain viewer to use the edit domain type js file in view mode to render the domain types. --- client/views/domain-viewer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/views/domain-viewer.js b/client/views/domain-viewer.js index edaff605a7..b4fbe8ae3a 100644 --- a/client/views/domain-viewer.js +++ b/client/views/domain-viewer.js @@ -25,7 +25,7 @@ var Plotly = require('../lib/plotly'); var Tooltips = require('../tooltips'); //views var View = require('ampersand-view'); -var TypesViewer = require('./view-domain-types'); +var TypesViewer = require('./edit-domain-type'); var SelectView = require('ampersand-select-view'); var ParticleViewer = require('./view-particle'); //templates @@ -171,9 +171,9 @@ module.exports = View.extend({ this.model.types, TypesViewer, this.queryByHook("domain-types-list"), - {"filter": function (model) { + {filter: function (model) { return model.typeID != 0; - }} + }, viewOptions: {viewMode: true}} ); }, displayDomain: function () { From ce6deb98bda1a1d9ff80ba720f1aa751a2bbc995 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 14:39:11 -0400 Subject: [PATCH 092/133] Removed the dead view-domain-types.js file. Renamed the domain type view file. --- ...mainTypesViewer.pug => viewDomainType.pug} | 0 client/views/view-domain-types.js | 26 ------------------- 2 files changed, 26 deletions(-) rename client/templates/includes/{domainTypesViewer.pug => viewDomainType.pug} (100%) delete mode 100644 client/views/view-domain-types.js diff --git a/client/templates/includes/domainTypesViewer.pug b/client/templates/includes/viewDomainType.pug similarity index 100% rename from client/templates/includes/domainTypesViewer.pug rename to client/templates/includes/viewDomainType.pug diff --git a/client/views/view-domain-types.js b/client/views/view-domain-types.js deleted file mode 100644 index 5b2d8906fa..0000000000 --- a/client/views/view-domain-types.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -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 . -*/ - -//views -var View = require('ampersand-view'); -//templates -var template = require('../templates/includes/domainTypesViewer.pug'); - -module.exports = View.extend({ - template: template, -}); \ No newline at end of file From 324329f418b75853a8b91d752a0c4aa0e9f0299b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 14:47:39 -0400 Subject: [PATCH 093/133] Re-designed the layout for view domain type. --- client/templates/includes/domainViewer.pug | 4 ++-- client/templates/includes/viewDomainType.pug | 23 ++++++++++++++------ client/views/edit-domain-type.js | 1 - 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/client/templates/includes/domainViewer.pug b/client/templates/includes/domainViewer.pug index cbfe1f2478..bc9ba9b3c1 100644 --- a/client/templates/includes/domainViewer.pug +++ b/client/templates/includes/domainViewer.pug @@ -117,7 +117,7 @@ div#domain-viewer.card div.mx-1.row.head.align-items-baseline - div.col-sm-2: h6.pl-2 Name + div.col-sm-2: h6 Name div.col-sm-2: h6 Number of Particles @@ -129,7 +129,7 @@ div#domain-viewer.card div.col-sm-2: h6 Fixed - div(data-hook="domain-types-list") + div.my-3(data-hook="domain-types-list") div.tab-pane.active(id="edit-domain" data-hook="edit-domain") diff --git a/client/templates/includes/viewDomainType.pug b/client/templates/includes/viewDomainType.pug index fab182ab9e..1baecacecc 100644 --- a/client/templates/includes/viewDomainType.pug +++ b/client/templates/includes/viewDomainType.pug @@ -1,13 +1,22 @@ -tr +div.mx-1 - td=this.model.name + if(this.model.collection.indexOf(this.model) !== 1) + hr - td=this.model.numParticles + div.row - td=this.model.mass + div.col-sm-2 - td=this.model.volume + div.pl-2=this.model.name - td=this.model.nu + div.col-sm-2=this.model.numParticles - td: input(type="checkbox" checked=this.model.fixed disabled) \ No newline at end of file + div.col-sm-2=this.model.mass + + div.col-sm-2=this.model.volume + + div.col-sm-2=this.model.nu + + div.col-sm-2 + + input(type="checkbox" checked=this.model.fixed disabled) diff --git a/client/views/edit-domain-type.js b/client/views/edit-domain-type.js index bd3b028c17..455cf02da3 100644 --- a/client/views/edit-domain-type.js +++ b/client/views/edit-domain-type.js @@ -36,7 +36,6 @@ module.exports = View.extend({ initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.viewMode = attrs.viewMode ? attrs.viewMode : false; - console.log(this.model.typeID) }, render: function (attrs, options) { this.template = this.viewMode ? viewTemplate : editTemplate; From c24f1e33240f5cdae1bb6dab4b9d2ec049f92efe Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 15:11:47 -0400 Subject: [PATCH 094/133] Fixed conflicting data hooks. Added function to hide the 'View External Domain' button when the view tab is clicked. --- client/templates/includes/domainViewer.pug | 2 +- client/views/domain-viewer.js | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/client/templates/includes/domainViewer.pug b/client/templates/includes/domainViewer.pug index bc9ba9b3c1..6682a5cf1d 100644 --- a/client/templates/includes/domainViewer.pug +++ b/client/templates/includes/domainViewer.pug @@ -137,7 +137,7 @@ div#domain-viewer.card div - button.btn.btn-outline-primary.box-shadow(data-hook="edit-domain") Edit Domain + button.btn.btn-outline-primary.box-shadow(data-hook="edit-domain-btn") Edit Domain button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="create-domain") Build New Domain diff --git a/client/views/domain-viewer.js b/client/views/domain-viewer.js index b4fbe8ae3a..0efa946f69 100644 --- a/client/views/domain-viewer.js +++ b/client/views/domain-viewer.js @@ -34,8 +34,10 @@ var template = require('../templates/includes/domainViewer.pug'); module.exports = View.extend({ template: template, events: { + 'click [data-hook=domain-edit-tab]' : 'toggleViewExternalDomainBtn', + 'click [data-hook=domain-view-tab]' : 'toggleViewExternalDomainBtn', 'click [data-hook=collapse]' : 'changeCollapseButtonText', - 'click [data-hook=edit-domain]' : 'editDomain', + 'click [data-hook=edit-domain-btn]' : 'editDomain', 'click [data-hook=create-domain]' : 'editDomain', 'click [data-hook=save-to-model]' : 'saveDomainToModel', 'click [data-hook=select-external-domain]' : 'handleLoadExternalDomain', @@ -213,6 +215,16 @@ module.exports = View.extend({ errorMsg.removeClass('component-invalid') } }, + toggleViewExternalDomainBtn: function (e) { + if(e) { + if(!e.target.classList.contains("active")) { + let display = e.target.text === "View" ? "none" : "block" + $(this.queryByHook("external-domains-container")).css("display", display); + } + }else{ + console.log("TODO: handle initial state") + } + }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); } From 2f55f2c6f7bc81bc20179b216b741246a08dca6a Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 23 Jun 2021 15:37:52 -0400 Subject: [PATCH 095/133] Added function to support read only mode. --- client/templates/includes/domainViewer.pug | 2 +- client/views/domain-viewer.js | 90 +++++++++++++--------- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/client/templates/includes/domainViewer.pug b/client/templates/includes/domainViewer.pug index 6682a5cf1d..14590cd722 100644 --- a/client/templates/includes/domainViewer.pug +++ b/client/templates/includes/domainViewer.pug @@ -111,7 +111,7 @@ div#domain-viewer.card h6="Number of Un-Assigned Particles: " + this.model.types.get(0, "typeID").numParticles - div(data-hook="domain-error"): p.text-danger A domain cannot have any un-assigned particles. + div.component-valid(data-hook="domain-error"): p.text-danger A domain cannot have any un-assigned particles. hr diff --git a/client/views/domain-viewer.js b/client/views/domain-viewer.js index 0efa946f69..0ac2e490bb 100644 --- a/client/views/domain-viewer.js +++ b/client/views/domain-viewer.js @@ -44,6 +44,55 @@ module.exports = View.extend({ 'change [data-hook=select-domain]' : 'handleSelectDomain', 'change [data-hook=select-location]' : 'handleSelectDomainLocation' }, + initialize: function (attrs, options) { + View.prototype.initialize.apply(this, arguments); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.tooltips = Tooltips.domainEditor; + this.domainPath = attrs.domainPath; + let self = this; + this.model.particles.forEach(function (particle) { + self.model.types.get(particle.type, "typeID").numParticles += 1; + }); + this.gravity = this.getGravityString() + }, + render: function () { + View.prototype.render.apply(this, arguments); + let self = this; + var queryStr = "?path=" + this.parent.model.directory + if(this.readOnly) { + $(this.queryByHook('domain-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('domain-view-tab')).tab('show'); + $(this.queryByHook('edit-domain')).removeClass('active'); + $(this.queryByHook('view-domain')).addClass('active'); + this.toggleViewExternalDomainBtn(); + }else { + this.renderDomainSelectView(); + if(this.domainPath) { + $(this.queryByHook("domain-container")).collapse("show") + $(this.queryByHook("collapse")).text("-") + if(this.domainPath !== "viewing") { + $(this.queryByHook("save-to-model")).prop("disabled", false) + $(this.queryByHook("external-domain-select")).css("display", "block") + $(this.queryByHook("select-external-domain")).text("View Model's Domain") + queryStr += "&domain_path=" + this.domainPath + } + } + this.toggleDomainError(); + } + this.renderTypesViewer(); + this.parent.renderParticleViewer(null); + let endpoint = path.join(app.getApiPath(), "spatial-model/domain-plot") + queryStr; + app.getXHR(endpoint, { + always: function (err, response, body) { + self.plot = body.fig; + self.displayDomain(); + } + }); + }, handleLoadExternalDomain: function (e) { let text = e.target.textContent if(text === "View External Domain") { @@ -80,48 +129,12 @@ module.exports = View.extend({ this.parent.renderDomainViewer(domainPath); } }, - initialize: function (attrs, options) { - View.prototype.initialize.apply(this, arguments); - this.tooltips = Tooltips.domainEditor; - this.domainPath = attrs.domainPath; - let self = this; - this.model.particles.forEach(function (particle) { - self.model.types.get(particle.type, "typeID").numParticles += 1; - }); - this.gravity = this.getGravityString() - }, getGravityString: function () { var gravity = "(X: " + this.model.gravity[0]; gravity += ", Y: " + this.model.gravity[1]; gravity += ", Z: " + this.model.gravity[2] + ")"; return gravity; }, - render: function () { - View.prototype.render.apply(this, arguments); - this.renderDomainSelectView(); - this.renderTypesViewer(); - let self = this; - var queryStr = "?path=" + this.parent.model.directory - if(this.domainPath) { - $(this.queryByHook("domain-container")).collapse("show") - $(this.queryByHook("collapse")).text("-") - if(this.domainPath !== "viewing") { - $(this.queryByHook("save-to-model")).prop("disabled", false) - $(this.queryByHook("external-domain-select")).css("display", "block") - $(this.queryByHook("select-external-domain")).text("View Model's Domain") - queryStr += "&domain_path=" + this.domainPath - } - } - this.parent.renderParticleViewer(null); - let endpoint = path.join(app.getApiPath(), "spatial-model/domain-plot") + queryStr; - app.getXHR(endpoint, { - always: function (err, response, body) { - self.plot = body.fig; - self.displayDomain(); - } - }); - this.toggleDomainError(); - }, renderDomainSelectView: function () { let self = this; let endpoint = path.join(app.getApiPath(), "spatial-model/domain-list"); @@ -218,11 +231,12 @@ module.exports = View.extend({ toggleViewExternalDomainBtn: function (e) { if(e) { if(!e.target.classList.contains("active")) { - let display = e.target.text === "View" ? "none" : "block" + let display = e.target.text === "View" ? "none" : "block"; $(this.queryByHook("external-domains-container")).css("display", display); } }else{ - console.log("TODO: handle initial state") + let display = this.readOnly ? "none" : "block"; + $(this.queryByHook("external-domains-container")).css("display", display); } }, changeCollapseButtonText: function (e) { From 6bd08407bda18ecb09b37bbed21973c95734a7de Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 09:30:35 -0400 Subject: [PATCH 096/133] Added auto save function to the model that saves every two minutes. --- client/models/model.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/models/model.js b/client/models/model.js index b8b59ab441..80cbff9cf0 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -124,7 +124,10 @@ module.exports = Model.extend({ return id; }, autoSave: function () { - //TODO: implement auto save + let self = this; + setTimeout(function () { + app.postXHR(self.url(), self.toJSON(), { success: self.autoSave }); + }, 120000); }, //called when save button is clicked saveModel: function (cb=null) { From 2c14e27d8cfa9e179f77798546793cf2f922b749 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 09:31:28 -0400 Subject: [PATCH 097/133] Added function to the model editor that starts the autosave process. --- client/pages/model-editor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index b3b8160138..9f7300b9db 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -257,7 +257,8 @@ let ModelEditor = PageView.extend({ } this.renderSystemVolumeView(); } - $(document).ready(function () { + this.model.autoSave(); + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); From f165accaba237f137b2863ef7f8c154b419e6966 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 12:22:15 -0400 Subject: [PATCH 098/133] Cleaned up the initial conditions render function. Removed the initial conditions viewer." --- client/pages/model-editor.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index b3b8160138..48ec39bf49 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -30,7 +30,6 @@ var InputView = require('../views/input'); var DomainViewer = require('../views/domain-viewer'); var SpeciesEditorView = require('../views/species-editor'); var InitialConditionsEditorView = require('../views/initial-conditions-editor'); -var InitialConditionsViewer = require('../views/initial-conditions-viewer'); var ParametersEditorView = require('../views/parameters-editor'); var ParticleViewer = require('../views/view-particle'); var ReactionsEditorView = require('../views/reactions-editor'); @@ -306,22 +305,15 @@ let ModelEditor = PageView.extend({ }); app.registerRenderSubview(this, this.speciesEditor, 'species-editor-container'); }, - renderInitialConditions: function (mode="edit", opened=false) { + renderInitialConditions: function () { if(this.initialConditionsEditor) { this.initialConditionsEditor.remove(); } - if(mode === "edit") { - this.initialConditionsEditor = new InitialConditionsEditorView({ - collection: this.model.initialConditions, - opened: opened - }); - }else{ - this.initialConditionsEditor = new InitialConditionsViewer({ - collection: this.model.initialConditions - }); - } + this.initialConditionsEditor = new InitialConditionsEditorView({ + collection: this.model.initialConditions, + }); app.registerRenderSubview(this, this.initialConditionsEditor, 'initial-conditions-editor-container'); - }, + }, renderParametersView: function () { if(this.parametersEditor) { this.parametersEditor.remove() From b0bc2ea1cf3dfaef8308e4548e412ac6fe35b16b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 13:03:38 -0400 Subject: [PATCH 099/133] Re-designed the layout for initial conditions. --- .../includes/editInitialCondition.pug | 39 +++++---- .../includes/initialConditionsEditor.pug | 85 +++++++++++-------- 2 files changed, 75 insertions(+), 49 deletions(-) diff --git a/client/templates/includes/editInitialCondition.pug b/client/templates/includes/editInitialCondition.pug index 51d9602e52..9b055ac852 100644 --- a/client/templates/includes/editInitialCondition.pug +++ b/client/templates/includes/editInitialCondition.pug @@ -1,21 +1,30 @@ -tr +div.mx-1 - td: div(data-hook="initial-condition-type") + if(this.model.collection.indexOf(this.model) !== 0) + hr - td: div(data-hook="initial-condition-species") + div.row - td: div(data-hook="count-container") + div.col-sm-2 - td + div.pl-2(data-hook="initial-condition-type") - div(data-hook="place-details") - span Location: - div - div.inline(data-hook="x-container") - div.inline(data-hook="y-container") - div.inline(data-hook="z-container") - div(data-hook="scatter-details") - span Active in Types: - div(data-hook="initial-condition-types") + div.col-sm-2(data-hook="initial-condition-species") - td: button.btn.btn-outline-secondary(data-hook="remove") X \ No newline at end of file + div.col-sm-3(data-hook="count-container") + + div.col-sm-3 + + div(data-hook="place-details") + span Location: + div + div.inline(data-hook="x-container") + div.inline(data-hook="y-container") + div.inline(data-hook="z-container") + div(data-hook="scatter-details") + span Active in Types: + div(data-hook="initial-condition-types") + + div.col-sm-2 + + button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/initialConditionsEditor.pug b/client/templates/includes/initialConditionsEditor.pug index 935e31f578..385c3a8755 100644 --- a/client/templates/includes/initialConditionsEditor.pug +++ b/client/templates/includes/initialConditionsEditor.pug @@ -1,51 +1,68 @@ -div#initial-conditions.card.card-body +div#initial-conditions.card - div + div.card-header.pb-0 - h3.inline Initial Conditions + h3.inline.mr-3 Initial Conditions - button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#initial-condition" data-hook="initial-condition-button") + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="initial-conditions-tabs") + + li.nav-item + + a.nav-link.tab.active(data-hook="initial-conditions-edit-tab" data-toggle="tab" href="#edit-initial-conditions") Edit - div.collapse(id="initial-condition" data-hook="initial-conditions") + li.nav-item - p + a.nav-link.tab(data-hook="initial-conditions-view-tab" data-toggle="tab" href="#view-initial-conditions") View + + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#initial-condition" data-hook="initial-condition-button") + + + div.card-body + p.mb-0 | Define the initial conditions for a spatial simulation - ul + ul.mb-0 li A 'Scatter' initial condition distributes Count particles over the chosen types. li A 'Place' initial condition places Count particles at a given X, Y, Z coordinate. li A 'Distribute Uniformly per Voxel' initial condition puts Count particles in each voxel of the chosen types. - table.table + div.collapse.tab-content(id="initial-condition" data-hook="initial-conditions") + + div.tab-pane.active(id="edit-initial-conditions" data-hook="edit-initial-conditions") + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-sm-2: h6 Type + + div.col-sm-2: h6 Variable + + div.col-sm-3: h6 Count + + div.col-sm-3: h6 Details + + div.col-sm-2: h6 Remove - thead - - tr - - th(scope="col") Type - - th(scope="col") Variable + div.my-3(data-hook="initial-conditions-collection") - th(scope="col") Count - - th(scope="col") Details - - th(scope="col") Remove + div.dropdown.inline - tbody(data-hook="initial-conditions-collection") + button.btn.btn-outline-primary.box-shadow.dropdown-toggle#addInitialConditionBtn( + data-hook='add-initial-condition', + data-toggle='dropdown', + aria-haspopup='true', + aria-expanded='false', + type='button' + ) Add Initial Condition - div.dropdown.inline + ul.dropdown-menu(aria-labelledby='addInitialConditionBtn') + li.dropdown-item(data-hook='scatter') Scatter + li.dropdown-item(data-hook='place') Place + li.dropdown-item(data-hook='distribute-uniformly') Distribute Uniformly per Voxel - button.btn.btn-outline-primary.box-shadow.dropdown-toggle#addInitialConditionBtn( - data-hook='add-initial-condition', - data-toggle='dropdown', - aria-haspopup='true', - aria-expanded='false', - type='button' - ) Add Initial Condition + div.tab-pane(id="view-initial-conditions" data-hook="view-initial-conditions") - ul.dropdown-menu(aria-labelledby='addInitialConditionBtn') - li.dropdown-item(data-hook='scatter') Scatter - li.dropdown-item(data-hook='place') Place - li.dropdown-item(data-hook='distribute-uniformly') Distribute Uniformly per Voxel + hr - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-initial-conditions") Save Initial Conditions \ No newline at end of file + div.mx-1.row.head.align-items-baseline From 88927d50f97174932693f86d999afb4065ff20e8 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 16:03:57 -0400 Subject: [PATCH 100/133] Added annotation tooltip to the initial conditions editor. --- client/tooltips.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/tooltips.js b/client/tooltips.js index 42e8c77241..1b20ddb2e1 100644 --- a/client/tooltips.js +++ b/client/tooltips.js @@ -40,6 +40,9 @@ module.exports = { "of a variables after a given time step. This value will be used if a 'Minimum Value' not provided.
Minimum Value For Switching - Minimum population value "+ "at which variables will be represented as Concentration." }, + initialConditionsEditor: { + annotation: "An optional note about the initial conditions." + }, parametersEditor: { name: "Unique identifier for Parameter. Cannot share a name with other model components.", From 2d3a6b7a5682d8e4086613338ce1c006c063170f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 16:05:44 -0400 Subject: [PATCH 101/133] Added the annotations header. Cleaned up the render functions. Added the render function for the view tab. --- .../includes/editInitialCondition.pug | 6 ++- .../includes/initialConditionsEditor.pug | 20 +++++-- client/views/initial-conditions-editor.js | 53 ++++++++++--------- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/client/templates/includes/editInitialCondition.pug b/client/templates/includes/editInitialCondition.pug index 9b055ac852..839f469b41 100644 --- a/client/templates/includes/editInitialCondition.pug +++ b/client/templates/includes/editInitialCondition.pug @@ -11,9 +11,9 @@ div.mx-1 div.col-sm-2(data-hook="initial-condition-species") - div.col-sm-3(data-hook="count-container") + div.col-sm-2(data-hook="count-container") - div.col-sm-3 + div.col-sm-2 div(data-hook="place-details") span Location: @@ -25,6 +25,8 @@ div.mx-1 span Active in Types: div(data-hook="initial-condition-types") + div.col-sm-2 + div.col-sm-2 button.btn.btn-outline-secondary(data-hook="remove") X diff --git a/client/templates/includes/initialConditionsEditor.pug b/client/templates/includes/initialConditionsEditor.pug index 385c3a8755..c7380d3142 100644 --- a/client/templates/includes/initialConditionsEditor.pug +++ b/client/templates/includes/initialConditionsEditor.pug @@ -38,13 +38,15 @@ div#initial-conditions.card div.col-sm-2: h6 Variable - div.col-sm-3: h6 Count + div.col-sm-2: h6 Count - div.col-sm-3: h6 Details + div.col-sm-2: h6 Details + + div.col-sm-2: h6 Annotation div.col-sm-2: h6 Remove - div.my-3(data-hook="initial-conditions-collection") + div.my-3(data-hook="edit-initial-conditions-collection") div.dropdown.inline @@ -66,3 +68,15 @@ div#initial-conditions.card hr div.mx-1.row.head.align-items-baseline + + div.col-sm-2: h6 Type + + div.col-sm-2: h6 Variable + + div.col-sm-3: h6 Count + + div.col-sm-3: h6 Details + + div.col-sm-2: h6 Annotation + + div.my-3(data-hook="view-initial-conditions-collection") diff --git a/client/views/initial-conditions-editor.js b/client/views/initial-conditions-editor.js index 5e017ecde2..ae35950ea2 100644 --- a/client/views/initial-conditions-editor.js +++ b/client/views/initial-conditions-editor.js @@ -19,6 +19,7 @@ along with this program. If not, see . var $ = require('jquery'); //support files let app = require('../app'); +let Tooltips = require('../tooltips'); //views var View = require('ampersand-view'); var EditInitialCondition = require('./edit-initial-condition'); @@ -36,22 +37,12 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); - this.opened = attrs.opened; + this.tooltips = Tooltips.initialConditionEditor; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderCollection( - this.collection, - EditInitialCondition, - this.queryByHook('initial-conditions-collection') - ); - if(this.opened) { - this.openInitialConditionContainer(); - } - }, - update: function () { - }, - updateValid: function () { + this.renderEditInitialConditionsView(); + this.renderViewInitialConditionsView(); }, addInitialCondition: function (e) { var initialConditionType = e.target.textContent; @@ -63,19 +54,33 @@ module.exports = View.extend({ }else { var types = []; } - console.log(initialConditionType, types) this.collection.addInitialCondition(initialConditionType, types); }, - switchToViewMode: function (e) { - this.parent.modelStateButtons.clickSaveHandler(e); - this.parent.renderInitialConditions(mode="view"); - }, - openInitialConditionContainer: function () { - $(this.queryByHook('initial-conditions')).collapse('show'); - let collapseBtn = $(this.queryByHook('initial-condition-button')) - collapseBtn.trigger('click') - }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); - } + }, + renderEditInitialConditionsView: function () { + if(this.editInitialConditionView) { + this.editInitialConditionView.remove() + } + this.editInitialConditionView = this.renderCollection( + this.collection, + EditInitialCondition, + this.queryByHook('edit-initial-conditions-collection') + ); + }, + renderViewInitialConditionsView: function () { + if(this.viewInitialConditionView) { + this.viewInitialConditionView.remove() + } + let options = {viewOptions: {viewMode: true}}; + this.viewInitialConditionView = this.renderCollection( + this.collection, + EditInitialCondition, + this.queryByHook('view-initial-conditions-collection'), + options + ); + }, + update: function () {}, + updateValid: function () {} }); \ No newline at end of file From 47ba65c7eee6e426dfd39d8a92fb61d102571c43 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 16:26:04 -0400 Subject: [PATCH 102/133] Fixed timestepSize key error in notebook gen. Fixed event delay as str erro in notebook gen. --- stochss/handlers/util/stochss_notebook.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stochss/handlers/util/stochss_notebook.py b/stochss/handlers/util/stochss_notebook.py index 41840f149e..fae8ece046 100644 --- a/stochss/handlers/util/stochss_notebook.py +++ b/stochss/handlers/util/stochss_notebook.py @@ -137,7 +137,7 @@ def __create_event_strings(self, model, pad): pad=pad) a_names = self.__create_event_assignment_strings(assignments=assignments, event=event, pad=pad) - delay = f"{event['delay']}" if event['delay'] else None + delay = f'"{event["delay"]}"' if event['delay'] else None ev_str = f'{pad}self.add_event(Event(name="{event["name"]}", ' ev_str += f'trigger={t_name}, assignments=[{a_names}], ' ev_str += f'delay={delay}, priority="{event["priority"]}", ' @@ -770,9 +770,9 @@ def __create_stoich_spec_string(self, stoich_species): def __create_tspan_string(self, model, pad): end = self.s_model['modelSettings']['endSim'] output_freq = self.s_model['modelSettings']['timeStep'] - step_size = self.s_model['modelSettings']['timestepSize'] tspan = ["", f"{pad}# Timespan"] if self.s_model['is_spatial']: + step_size = self.s_model['modelSettings']['timestepSize'] ts_str = f'{pad}self.timespan(np.arange(0, {end + step_size}, {output_freq})' ts_str += f", timestep_size={step_size})" else: From 4796288de2c81f04430fb37e65103b004d6dd38d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 16:53:11 -0400 Subject: [PATCH 103/133] Added annotations to the initial conditions model. --- client/models/initial-condition.js | 1 + client/models/initial-conditions.js | 1 + client/templates/includes/editInitialCondition.pug | 6 +++++- client/views/edit-initial-condition.js | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/models/initial-condition.js b/client/models/initial-condition.js index 8f4801b10f..26c2f1cae1 100644 --- a/client/models/initial-condition.js +++ b/client/models/initial-condition.js @@ -23,6 +23,7 @@ var Specie = require('./specie'); module.exports = State.extend({ props: { icType: 'string', + annotation: 'string', count: 'number', types: 'object', x: 'number', diff --git a/client/models/initial-conditions.js b/client/models/initial-conditions.js index c6984f1a16..ebd1e89ce9 100644 --- a/client/models/initial-conditions.js +++ b/client/models/initial-conditions.js @@ -26,6 +26,7 @@ module.exports = Collection.extend({ addInitialCondition: function (initialConditionType, types) { var initialCondition = new InitialCondition({ icType: initialConditionType, + annotation: "", types: types, count: 0, x: 0, diff --git a/client/templates/includes/editInitialCondition.pug b/client/templates/includes/editInitialCondition.pug index 839f469b41..3b29326c07 100644 --- a/client/templates/includes/editInitialCondition.pug +++ b/client/templates/includes/editInitialCondition.pug @@ -25,7 +25,11 @@ div.mx-1 span Active in Types: div(data-hook="initial-condition-types") - div.col-sm-2 + div.col-sm-2 + + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") + + button.btn.btn-outline-secondary.btn-sm.box-shadow(data-hook="edit-annotation-btn") Edit div.col-sm-2 diff --git a/client/views/edit-initial-condition.js b/client/views/edit-initial-condition.js index 11089e5efc..e71b463ddc 100644 --- a/client/views/edit-initial-condition.js +++ b/client/views/edit-initial-condition.js @@ -41,6 +41,9 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); + if(!this.model.annotation){ + $(this.queryByHook('edit-annotation-btn')).text('Add') + } var self = this; var options = ['Scatter', 'Place', 'Distribute Uniformly per Voxel']; var typeSelectView = new SelectView({ From 3ec163687fae71872001a4b7845c64a9af974f80 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 24 Jun 2021 17:02:21 -0400 Subject: [PATCH 104/133] Added tooltip icon to initial conditions annotation header. Added function to hide annotations header in view mode if no initial conditions have annotations. --- client/templates/includes/initialConditionsEditor.pug | 8 ++++++-- client/views/initial-conditions-editor.js | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/client/templates/includes/initialConditionsEditor.pug b/client/templates/includes/initialConditionsEditor.pug index c7380d3142..275629091f 100644 --- a/client/templates/includes/initialConditionsEditor.pug +++ b/client/templates/includes/initialConditionsEditor.pug @@ -42,7 +42,11 @@ div#initial-conditions.card div.col-sm-2: h6 Details - div.col-sm-2: h6 Annotation + div.col-sm-2 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) div.col-sm-2: h6 Remove @@ -77,6 +81,6 @@ div#initial-conditions.card div.col-sm-3: h6 Details - div.col-sm-2: h6 Annotation + div.col-sm-2(data-hook="initial-conditions-annotation-header"): h6 Annotation div.my-3(data-hook="view-initial-conditions-collection") diff --git a/client/views/initial-conditions-editor.js b/client/views/initial-conditions-editor.js index ae35950ea2..7177ba2b82 100644 --- a/client/views/initial-conditions-editor.js +++ b/client/views/initial-conditions-editor.js @@ -37,7 +37,7 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); - this.tooltips = Tooltips.initialConditionEditor; + this.tooltips = Tooltips.initialConditionsEditor; }, render: function () { View.prototype.render.apply(this, arguments); @@ -73,6 +73,12 @@ module.exports = View.extend({ if(this.viewInitialConditionView) { this.viewInitialConditionView.remove() } + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("initial-conditions-annotation-header")).css("display", "none"); + }else{ + $(this.queryByHook("initial-conditions-annotation-header")).css("display", "block"); + } let options = {viewOptions: {viewMode: true}}; this.viewInitialConditionView = this.renderCollection( this.collection, From 20fb5eb861ce41478a2fb5a8571bb36e8e04380e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 09:25:28 -0400 Subject: [PATCH 105/133] Cleaned up the edit initial coditions view and added functions to allow users to update the annotation. --- client/views/edit-initial-condition.js | 212 +++++++++++++--------- client/views/initial-conditions-editor.js | 12 ++ 2 files changed, 142 insertions(+), 82 deletions(-) diff --git a/client/views/edit-initial-condition.js b/client/views/edit-initial-condition.js index e71b463ddc..231f12ddea 100644 --- a/client/views/edit-initial-condition.js +++ b/client/views/edit-initial-condition.js @@ -20,6 +20,7 @@ let $ = require('jquery'); //support files let app = require('../app'); var tests = require('./tests'); +let modals = require('../modals'); //views var View = require('ampersand-view'); var InputView = require('./input'); @@ -31,9 +32,10 @@ var template = require('../templates/includes/editInitialCondition.pug'); module.exports = View.extend({ template: template, events: { + 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', 'click [data-hook=remove]' : 'removeInitialCondition', 'change [data-hook=initial-condition-type]' : 'selectInitialConditionType', - 'change [data-hook=initial-condition-species]' : 'selectInitialConditionSpecies', + 'change [data-hook=initial-condition-species]' : 'selectInitialConditionSpecies' }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); @@ -41,87 +43,38 @@ module.exports = View.extend({ }, render: function () { View.prototype.render.apply(this, arguments); + $(document).on('shown.bs.modal', function (e) { + $('[autofocus]', e.target).focus(); + }); + $(document).on('hide.bs.modal', '.modal', function (e) { + e.target.remove() + }); if(!this.model.annotation){ $(this.queryByHook('edit-annotation-btn')).text('Add') } + this.toggleDetailsView(); + }, + editAnnotation: function () { var self = this; - var options = ['Scatter', 'Place', 'Distribute Uniformly per Voxel']; - var typeSelectView = new SelectView({ - label: '', - name: 'type', - required: true, - idAttributes: 'cid', - options: options, - value: self.model.icType, + var name = this.model.name; + var annotation = this.model.annotation; + if(document.querySelector('#initialConditionAnnotationModal')) { + document.querySelector('#initialConditionAnnotationModal').remove(); + } + let modal = $(modals.annotationModalHtml("initialCondition", name, annotation)).modal(); + let okBtn = document.querySelector('#initialConditionAnnotationModal .ok-model-btn'); + let input = document.querySelector('#initialConditionAnnotationModal #initialConditionAnnotationInput'); + input.addEventListener("keyup", function (event) { + if(event.keyCode === 13){ + event.preventDefault(); + okBtn.click(); + } }); - var speciesSelectView = new SelectView({ - label: '', - name: 'specie', - required: true, - idAttribute: 'cid', - textAttribute: 'name', - eagerValidate: true, - options: this.model.collection.parent.species, - // For new reactions (with no rate.name) just use the first parameter in the Parameters collection - // Else fetch the right Parameter from Parameters based on existing rate - value: this.model.specie.name ? this.getSpecieFromSpecies(this.model.specie.name) : this.model.collection.parent.species.at(0), + okBtn.addEventListener('click', function (e) { + self.model.annotation = input.value.trim(); + self.parent.renderEditInitialConditionsView(); + modal.modal('hide'); }); - app.registerRenderSubview(this, typeSelectView, 'initial-condition-type'); - app.registerRenderSubview(this, speciesSelectView, 'initial-condition-species'); - this.renderDetailsView(); - }, - update: function () { - }, - updateValid: function () { - }, - renderDetailsView: function () { - if(this.model.icType === "Place") { - $(this.queryByHook("scatter-details")).css("display", "none") - $(this.queryByHook("place-details")).css("display", "block") - this.renderLocation(); - }else { - $(this.queryByHook("place-details")).css("display", "none") - $(this.queryByHook("scatter-details")).css("display", "block") - this.renderTypes(); - } - }, - renderLocation: function () { - if(this.xCoord) { - this.xCoord.remove(); - this.yCoord.remove(); - this.zCoord.remove(); - } - this.xCoord = new InputView({parent: this, required: true, - name: 'X', valueType: 'number', - modelKey: "x", label: 'x: ', - tests: tests.valueTests, - value: this.model.x}); - app.registerRenderSubview(this, this.xCoord, "x-container"); - this.yCoord = new InputView({parent: this, required: true, - name: 'Y', valueType: 'number', - modelKey: "y", label: 'y: ', - tests: tests.valueTests, - value: this.model.y}); - app.registerRenderSubview(this, this.yCoord, "y-container"); - this.zCoord = new InputView({parent: this, required: true, - name: 'Z', valueType: 'number', - modelKey: "z", label: 'z: ', - tests: tests.valueTests, - value: this.model.z}); - app.registerRenderSubview(this, this.zCoord, "z-container"); - }, - renderTypes: function () { - if(this.typesView) { - this.typesView.remove(); - } - this.typesView = this.renderCollection( - this.model.collection.parent.domain.types, - TypesView, - this.queryByHook("initial-condition-types"), - {"filter": function (model) { - return model.typeID != 0; - }} - ); }, getSpecieFromSpecies: function (name) { var species = this.model.collection.parent.species.filter(function (specie) { @@ -132,6 +85,11 @@ module.exports = View.extend({ removeInitialCondition: function () { this.collection.removeInitialCondition(this.model); }, + selectInitialConditionSpecies: function (e) { + var name = e.target.selectedOptions.item(0).text; + var specie = this.getSpecieFromSpecies(name); + this.model.specie = specie || this.model.specie; + }, selectInitialConditionType: function (e) { var currentType = this.model.icType; var newType = e.target.selectedOptions.item(0).text; @@ -140,12 +98,45 @@ module.exports = View.extend({ this.renderDetailsView(); } }, - selectInitialConditionSpecies: function (e) { - var name = e.target.selectedOptions.item(0).text; - var specie = this.getSpecieFromSpecies(name); - this.model.specie = specie || this.model.specie; + toggleDetailsView: function () { + if(this.model.icType === "Place") { + $(this.queryByHook("scatter-details")).css("display", "none") + $(this.queryByHook("place-details")).css("display", "block") + }else { + $(this.queryByHook("place-details")).css("display", "none") + $(this.queryByHook("scatter-details")).css("display", "block") + } }, + update: function () {}, + updateValid: function () {}, subviews: { + selectICType: { + hook: 'initial-condition-type', + prepareView: function (el) { + let options = ['Scatter', 'Place', 'Distribute Uniformly per Voxel']; + return new SelectView({ + name: 'type', + required: true, + idAttributes: 'cid', + options: options, + value: this.model.icType, + }); + } + }, + selectSpecies: { + hook: 'initial-condition-species', + prepareView: function (el) { + return new SelectView({ + name: 'specie', + required: true, + idAttribute: 'cid', + textAttribute: 'name', + eagerValidate: true, + options: this.model.collection.parent.species, + value: this.model.specie.name ? this.getSpecieFromSpecies(this.model.specie.name) : this.model.collection.parent.species.at(0), + }); + } + }, inputCount: { hook: 'count-container', prepareView: function (el) { @@ -153,13 +144,70 @@ module.exports = View.extend({ parent: this, required: true, name: 'count', - label: '', tests: tests.valueTests, modelKey: 'count', valueType: 'number', value: this.model.count, }); - }, + } + }, + typesView: { + hook: 'initial-condition-types', + prepareView: function (el) { + return this.renderCollection( + this.model.collection.parent.domain.types, + TypesView, + this.queryByHook("initial-condition-types"), + {"filter": function (model) { + return model.typeID != 0; + }} + ); + } + }, + inputXCoord: { + hook: 'x-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'X', + valueType: 'number', + modelKey: "x", + label: 'x: ', + tests: tests.valueTests, + value: this.model.x + }); + } + }, + inputYCoord: { + hook: 'y-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'Y', + valueType: 'number', + modelKey: "y", + label: 'y: ', + tests: tests.valueTests, + value: this.model.y + }); + } + }, + inputZCoord: { + hook: 'z-container', + prepareView: function (el) { + return new InputView({ + parent: this, + required: true, + name: 'Z', + valueType: 'number', + modelKey: "z", + label: 'z: ', + tests: tests.valueTests, + value: this.model.z + }); + } } } }); \ No newline at end of file diff --git a/client/views/initial-conditions-editor.js b/client/views/initial-conditions-editor.js index 7177ba2b82..bbcd21440d 100644 --- a/client/views/initial-conditions-editor.js +++ b/client/views/initial-conditions-editor.js @@ -68,6 +68,12 @@ module.exports = View.extend({ EditInitialCondition, this.queryByHook('edit-initial-conditions-collection') ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); }, renderViewInitialConditionsView: function () { if(this.viewInitialConditionView) { @@ -86,6 +92,12 @@ module.exports = View.extend({ this.queryByHook('view-initial-conditions-collection'), options ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); }, update: function () {}, updateValid: function () {} From a8a5b3ff93513e809898b645edd6eef8c0d31875 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 09:33:10 -0400 Subject: [PATCH 106/133] Added the view template and template selection. --- client/views/edit-initial-condition.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/client/views/edit-initial-condition.js b/client/views/edit-initial-condition.js index 231f12ddea..e8290aa59f 100644 --- a/client/views/edit-initial-condition.js +++ b/client/views/edit-initial-condition.js @@ -27,10 +27,10 @@ var InputView = require('./input'); var SelectView = require('ampersand-select-view'); var TypesView = require('./component-types'); //templates -var template = require('../templates/includes/editInitialCondition.pug'); +let editTemplate = require('../templates/includes/editInitialCondition.pug'); +let viewTemplate = require('../templates/includes/viewInitialCondition.pug'); module.exports = View.extend({ - template: template, events: { 'click [data-hook=edit-annotation-btn]' : 'editAnnotation', 'click [data-hook=remove]' : 'removeInitialCondition', @@ -40,8 +40,18 @@ module.exports = View.extend({ initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.modelType = "initial-condition"; + this.viewMode = attrs.viewMode ? attrs.viewMode : false; + if(this.viewMode) { + let self = this; + this.types = []; + this.model.types.forEach(function (index) { + let type = self.model.collection.parent.domain.types.get(index, "typeID"); + self.types.push(type.name) + }); + } }, render: function () { + this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); From fd4455321fb88d961f7e8d6b6a80113c6f3602af Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 09:39:02 -0400 Subject: [PATCH 107/133] Re-designed the layout for view initial condition. --- .../includes/initialConditionsEditor.pug | 4 +- .../includes/viewInitialCondition.pug | 40 +++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/client/templates/includes/initialConditionsEditor.pug b/client/templates/includes/initialConditionsEditor.pug index 275629091f..bac19f8d8c 100644 --- a/client/templates/includes/initialConditionsEditor.pug +++ b/client/templates/includes/initialConditionsEditor.pug @@ -77,9 +77,9 @@ div#initial-conditions.card div.col-sm-2: h6 Variable - div.col-sm-3: h6 Count + div.col-sm-2: h6 Count - div.col-sm-3: h6 Details + div.col-sm-4: h6 Details div.col-sm-2(data-hook="initial-conditions-annotation-header"): h6 Annotation diff --git a/client/templates/includes/viewInitialCondition.pug b/client/templates/includes/viewInitialCondition.pug index 34f4017019..68d6623a66 100644 --- a/client/templates/includes/viewInitialCondition.pug +++ b/client/templates/includes/viewInitialCondition.pug @@ -1,17 +1,33 @@ -tr +div.mx-1 - td=this.model.icType + if(this.model.collection.indexOf(this.model) !== 0) + hr - td=this.model.specie.name + div.row - td=this.model.count + div.col-sm-2 - td + div.pl-2=this.model.icType - if this.model.icType === "Place" - span Location: - div="(x: "+this.model.x+", y: "+this.model.y+", z: "+this.model.z+")" - - else - span Active in Types: - div=this.types.join(", ") + div.col-sm-2 + + div=this.model.specie.name + + div.col-sm-2 + + div=this.model.count + + div.col-sm-4 + + if this.model.icType === "Place" + span Location: + div="(x: "+this.model.x+", y: "+this.model.y+", z: "+this.model.z+")" + + else + span Active in Types: + div=this.types.join(", ") + + div.col-sm-2 + + if this.model.annotation + div.tooltip-icon-large(data-hook="annotation-tooltip" data-html="true" data-toggle="tooltip" title=this.model.annotation || "Click 'Add' to add an annotation") From 57b976a45bd886d9642d3eecdeba58513394f2f0 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 09:53:46 -0400 Subject: [PATCH 108/133] Added functions to update the view tap when the model is changed. --- client/views/component-types.js | 4 +--- client/views/edit-initial-condition.js | 10 +++++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/client/views/component-types.js b/client/views/component-types.js index 212ef27413..8c14752568 100644 --- a/client/views/component-types.js +++ b/client/views/component-types.js @@ -46,8 +46,6 @@ module.exports = View.extend({ let index = this.parent.model.types.indexOf(typeID); this.parent.model.types.splice(index, 1); } - if(this.parent.modelType === "species") { - this.parent.model.trigger('change'); - } + this.parent.model.trigger('change'); } }); \ No newline at end of file diff --git a/client/views/edit-initial-condition.js b/client/views/edit-initial-condition.js index e8290aa59f..7430c3a167 100644 --- a/client/views/edit-initial-condition.js +++ b/client/views/edit-initial-condition.js @@ -17,6 +17,7 @@ along with this program. If not, see . */ let $ = require('jquery'); +let _ = require('underscore'); //support files let app = require('../app'); var tests = require('./tests'); @@ -53,6 +54,9 @@ module.exports = View.extend({ render: function () { this.template = this.viewMode ? viewTemplate : editTemplate; View.prototype.render.apply(this, arguments); + if(!this.viewMode) { + this.model.on('change', _.bind(this.updateViewer, this)); + } $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); @@ -99,13 +103,14 @@ module.exports = View.extend({ var name = e.target.selectedOptions.item(0).text; var specie = this.getSpecieFromSpecies(name); this.model.specie = specie || this.model.specie; + this.model.trigger('change'); }, selectInitialConditionType: function (e) { var currentType = this.model.icType; var newType = e.target.selectedOptions.item(0).text; this.model.icType = newType; if(currentType === "Place" || newType === "Place"){ - this.renderDetailsView(); + this.toggleDetailsView(); } }, toggleDetailsView: function () { @@ -119,6 +124,9 @@ module.exports = View.extend({ }, update: function () {}, updateValid: function () {}, + updateViewer: function () { + this.parent.renderViewInitialConditionsView(); + }, subviews: { selectICType: { hook: 'initial-condition-type', From 4a89e2e5c9fe3ae33831a7ede45572aab96272df Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 10:02:59 -0400 Subject: [PATCH 109/133] Added function to support read only mode. --- client/views/initial-conditions-editor.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/client/views/initial-conditions-editor.js b/client/views/initial-conditions-editor.js index bbcd21440d..bfbc38808e 100644 --- a/client/views/initial-conditions-editor.js +++ b/client/views/initial-conditions-editor.js @@ -38,10 +38,22 @@ module.exports = View.extend({ initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); this.tooltips = Tooltips.initialConditionsEditor; + this.readOnly = attrs.readOnly ? attrs.readOnly : false; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderEditInitialConditionsView(); + if(this.readOnly) { + $(this.queryByHook('initial-conditions-edit-tab')).addClass("disabled"); + $(".nav .disabled>a").on("click", function(e) { + e.preventDefault(); + return false; + }); + $(this.queryByHook('initial-conditions-view-tab')).tab('show'); + $(this.queryByHook('edit-initial-conditions')).removeClass('active'); + $(this.queryByHook('view-initial-conditions')).addClass('active'); + }else{ + this.renderEditInitialConditionsView(); + } this.renderViewInitialConditionsView(); }, addInitialCondition: function (e) { From aadad1fac814420745e6dc1f5367bd7379b5ba71 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 11:32:44 -0400 Subject: [PATCH 110/133] Removed empty directories. Updated the model template test. --- .../tests/mock_file_sys/sbml_files/test1.sbml | 137 ------------------ .../tests/mock_file_sys/sbml_files/test2.sbml | 1 - stochss/tests/run_tests.py | 4 +- stochss/tests/test_model_template.py | 23 ++- 4 files changed, 12 insertions(+), 153 deletions(-) delete mode 100644 stochss/tests/mock_file_sys/sbml_files/test1.sbml delete mode 100644 stochss/tests/mock_file_sys/sbml_files/test2.sbml diff --git a/stochss/tests/mock_file_sys/sbml_files/test1.sbml b/stochss/tests/mock_file_sys/sbml_files/test1.sbml deleted file mode 100644 index edd42e3c79..0000000000 --- a/stochss/tests/mock_file_sys/sbml_files/test1.sbml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - x - - - y - - - - x - y - - - - - - - - - - - - - - - - - - - - - - 0.5 - - - - - - - - 8000 - - - s1 - 1 - - - - - - - - - - - - - - - - k2 - - - - - - - - - - - - k1 - s1 - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - t - 50 - - - - - - - - t - 40 - - - - - - 0 - - - - - - 2000 - - - - - - - diff --git a/stochss/tests/mock_file_sys/sbml_files/test2.sbml b/stochss/tests/mock_file_sys/sbml_files/test2.sbml deleted file mode 100644 index a87bc5eb1c..0000000000 --- a/stochss/tests/mock_file_sys/sbml_files/test2.sbml +++ /dev/null @@ -1 +0,0 @@ -{"is_spatial":false,"defaultID":10,"defaultMode":"discrete","modelSettings":{"endSim":20,"timeStep":0.05,"volume":1,"algorithm":"Hybrid-Tau-Leaping","realizations":1},"simulationSettings":{"isAutomatic":true,"relativeTol":0.001,"absoluteTol":0.000001,"realizations":1,"algorithm":"SSA","seed":-1,"tauTol":0.03},"parameterSweepSettings":{"is1D":true,"p1Min":0,"p1Max":0,"p1Steps":11,"p2Min":0,"p2Max":0,"p2Steps":11,"parameterOne":{},"parameterTwo":{},"speciesOfInterest":{"subdomains":[]}},"meshSettings":{"count":2},"species":[{"compID":1,"name":"s1","value":500,"mode":"discrete","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]}],"initialConditions":[],"parameters":[{"compID":2,"name":"k1","expression":"0.5","annotation":""},{"compID":3,"name":"k2","expression":"0.1*500**-5","annotation":""}],"reactions":[{"compID":4,"name":"r1","reactionType":"creation","summary":"\\emptyset \\rightarrow s1","massaction":false,"propensity":"","annotation":"","subdomains":["subdomain 1:","subdomain 2:"],"rate":{"compID":3,"name":"k2","expression":"math.sin(1)","annotation":""},"reactants":[],"products":[{"ratio":1,"specie":{"compID":1,"name":"s1","value":500,"mode":"continuous","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]}}]},{"compID":5,"name":"r2","reactionType":"destruction","summary":"s1 \\rightarrow \\emptyset","massaction":false,"propensity":"","annotation":"","subdomains":["subdomain 1:","subdomain 2:"],"rate":{"compID":2,"name":"k1","expression":"0.5","annotation":""},"reactants":[{"ratio":1,"specie":{"compID":1,"name":"s1","value":500,"mode":"continuous","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]}}],"products":[]},{"compID":6,"name":"r3","reactionType":"custom-propensity","summary":"2s1 \\rightarrow 3s1","massaction":false,"propensity":"sin(1)","annotation":"","subdomains":["subdomain 1:","subdomain 2:"],"rate":{},"reactants":[{"ratio":2,"specie":{"compID":1,"name":"s1","value":500,"mode":"discrete","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]}}],"products":[{"ratio":3,"specie":{"compID":1,"name":"s1","value":500,"mode":"discrete","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]}}]}],"rules":[{"compID":8,"name":"rr1","type":"Rate Rule","variable":{"compID":1,"name":"s1","value":500,"mode":"discrete","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]},"expression":"sin(0.5)","annotation":""},{"compID":9,"name":"rr2","type":"Assignment Rule","variable":{"compID":1,"name":"s1","value":500,"mode":"discrete","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]},"expression":"8000","annotation":""}],"eventsCollection":[{"compID":7,"name":"e1","annotation":"","delay":"t-40","priority":"1","triggerExpression":"t>50","initialValue":true,"persistent":true,"useValuesFromTriggerTime":true,"eventAssignments":[{"expression":"2000","variable":{"compID":1,"name":"s1","value":500,"mode":"discrete","switchTol":0.03,"switchMin":100,"isSwitchTol":true,"annotation":"","diffusionCoeff":0,"subdomains":["subdomain 1:","subdomain 2:"]}}]}],"functionDefinitions":[]} \ No newline at end of file diff --git a/stochss/tests/run_tests.py b/stochss/tests/run_tests.py index 5aa6cce0f0..ea0b657c89 100755 --- a/stochss/tests/run_tests.py +++ b/stochss/tests/run_tests.py @@ -34,7 +34,7 @@ args = parser.parse_args() print(os.path.dirname(__file__)) - # import test_model_template + import test_model_template # import test_settings_template # import test_convert_sbml # import test_upload_file @@ -45,7 +45,7 @@ # import test_duplicate modules = [ - # test_model_template, + test_model_template, # test_settings_template, # test_convert_sbml, # test_rename, diff --git a/stochss/tests/test_model_template.py b/stochss/tests/test_model_template.py index d40e639bd5..94727bb219 100644 --- a/stochss/tests/test_model_template.py +++ b/stochss/tests/test_model_template.py @@ -4,16 +4,17 @@ os.chdir('/stochss') -template_path = "stochss_templates/nonSpatialModelTemplate.json" - - class TestNonSpatialModelTemplate(unittest.TestCase): - def test_model_elements(self): + def setUp(self): + template_path = "stochss_templates/nonSpatialModelTemplate.json" + with open(template_path, "r") as template_file: - template = json.load(template_file) + self.template = json.load(template_file) + - template_keys = sorted(list(template.keys())) + def test_model_elements(self): + template_keys = sorted(list(self.template.keys())) model_path = "client/models/model.js" with open(model_path, "r") as model_file: @@ -24,21 +25,17 @@ def test_model_elements(self): model_keys = list(map(lambda item: item.strip().split(':')[0], props)) model_keys.extend(list(map(lambda item: item.strip().split(':')[0], collections))) model_keys.extend(list(map(lambda item: item.strip().split(':')[0], children))) - + model_keys.sort() self.assertEqual(template_keys, model_keys) def test_model_settings_elements(self): - with open(template_path, "r") as template_file: - template = json.load(template_file) - - template_keys = sorted(list(template['modelSettings'].keys())) + template_keys = sorted(list(self.template['modelSettings'].keys())) mdl_settings_path = "client/models/model-settings.js" - + with open(mdl_settings_path, "r") as mdl_settings_file: data = mdl_settings_file.read().split("props: {").pop().split('}')[0].split(',') mdl_settings_keys = sorted(list(map(lambda item: item.strip().split(':')[0], data))) self.assertEqual(template_keys, mdl_settings_keys) - From 81636cdc0254f479bea03cfab2f0646bbbe4ff91 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 12:13:02 -0400 Subject: [PATCH 111/133] Refactored #if DOCKER_WORKING_DIR is defined and its value does not exist, create a directory at its path and copy Examples into it bash -c "if [ ! -d "/Users/owner/Documents/Undergrade_Research/Dr_Drawert/stochss/local_data" ] && [ -z ]; then mkdir /Users/owner/Documents/Undergrade_Research/Dr_Drawert/stochss/local_data; mkdir /Users/owner/Documents/Undergrade_Research/Dr_Drawert/stochss/local_data/Examples;cp -r public_models/* /Users/owner/Documents/Undergrade_Research/Dr_Drawert/stochss/local_data/Examples;fi" docker run --rm \ --name stochss-lab \ --env-file .env \ -v /Users/owner/Documents/Undergrade_Research/Dr_Drawert/stochss:/stochss \ -v /Users/owner/Documents/Undergrade_Research/Dr_Drawert/stochss/local_data:/home/jovyan/ \ -p 8888:8888 \ stochss-lab:latest \ /stochss/stochss/tests/run_tests.py /stochss/stochss/tests Executing: ====================================================================== to only rebuild the backend before running the unit tests. Add to the make file to rebuild the image and container before running the test. --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2bbfecc7e1..ed4b61a4e7 100644 --- a/Makefile +++ b/Makefile @@ -114,7 +114,7 @@ build: deps webpack --build-arg JUPYTER_CONFIG_DIR=$(JUPYTER_CONFIG_DIR) \ -t $(DOCKER_STOCHSS_IMAGE):latest . -test: build +test: create_working_dir docker run --rm \ --name $(DOCKER_STOCHSS_IMAGE) \ --env-file .env \ @@ -124,6 +124,8 @@ test: build $(DOCKER_STOCHSS_IMAGE):latest \ /stochss/stochss/tests/run_tests.py +build_and_test: build test + run: create_working_dir docker run --rm \ --name $(DOCKER_STOCHSS_IMAGE) \ From 1572cb6b54fd8afef89f3635db8e31c88963e6e7 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 12:15:40 -0400 Subject: [PATCH 112/133] Added a test for the domain in the model template. Pylint changes. --- stochss/tests/test_model_template.py | 59 ++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/stochss/tests/test_model_template.py b/stochss/tests/test_model_template.py index 94727bb219..b8351b42e3 100644 --- a/stochss/tests/test_model_template.py +++ b/stochss/tests/test_model_template.py @@ -1,12 +1,37 @@ +''' +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 unittest import json import os os.chdir('/stochss') -class TestNonSpatialModelTemplate(unittest.TestCase): - +class TestModelTemplate(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for model template complete ness. + ################################################################################################ + ''' def setUp(self): + ''' + Get the model template prior to each test. + ''' template_path = "stochss_templates/nonSpatialModelTemplate.json" with open(template_path, "r") as template_file: @@ -14,6 +39,9 @@ def setUp(self): def test_model_elements(self): + ''' + Check if the model template has all of the properties currently in the model. + ''' template_keys = sorted(list(self.template.keys())) model_path = "client/models/model.js" @@ -30,12 +58,35 @@ def test_model_elements(self): self.assertEqual(template_keys, model_keys) - def test_model_settings_elements(self): + def test_timespan_settings_elements(self): + ''' + Check if the timespan settings in the model template has + all of the properties currently in the timespan settings. + ''' template_keys = sorted(list(self.template['modelSettings'].keys())) - mdl_settings_path = "client/models/model-settings.js" + mdl_settings_path = "client/models/timespan-settings.js" with open(mdl_settings_path, "r") as mdl_settings_file: data = mdl_settings_file.read().split("props: {").pop().split('}')[0].split(',') mdl_settings_keys = sorted(list(map(lambda item: item.strip().split(':')[0], data))) self.assertEqual(template_keys, mdl_settings_keys) + + + def test_model_domain_elements(self): + ''' + Check if the domain in the model template has all of the + properties currently in the domain. + ''' + template_keys = sorted(list(self.template['domain'].keys())) + domain_path = "client/models/domain.js" + + with open(domain_path, "r") as domain_file: + data = domain_file.read() + props = data.split("props: {").pop().split('}')[0].split(',') + collections = data.split("collections: {").pop().split('}')[0].split(',') + domain_keys = list(map(lambda item: item.strip().split(':')[0], props)) + domain_keys.extend(list(map(lambda item: item.strip().split(':')[0], collections))) + + domain_keys.sort() + self.assertEqual(template_keys, domain_keys) From 5140c6224a44f8998deb9798f1d2e099166ca237 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 13:24:24 -0400 Subject: [PATCH 113/133] Cleaned up the settings template tests. Added a test for timespan settings. Pylint updates. --- stochss/tests/test_model_template.py | 8 +-- stochss/tests/test_settings_template.py | 96 ++++++++++++++++++------- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/stochss/tests/test_model_template.py b/stochss/tests/test_model_template.py index b8351b42e3..c2bb805551 100644 --- a/stochss/tests/test_model_template.py +++ b/stochss/tests/test_model_template.py @@ -25,7 +25,7 @@ class TestModelTemplate(unittest.TestCase): ''' ################################################################################################ - Unit tests for model template complete ness. + Unit tests for model template completeness. ################################################################################################ ''' def setUp(self): @@ -40,7 +40,7 @@ def setUp(self): def test_model_elements(self): ''' - Check if the model template has all of the properties currently in the model. + Check if the model template has all of the properties currently in the model model. ''' template_keys = sorted(list(self.template.keys())) model_path = "client/models/model.js" @@ -61,7 +61,7 @@ def test_model_elements(self): def test_timespan_settings_elements(self): ''' Check if the timespan settings in the model template has - all of the properties currently in the timespan settings. + all of the properties currently in the timespan settings model. ''' template_keys = sorted(list(self.template['modelSettings'].keys())) mdl_settings_path = "client/models/timespan-settings.js" @@ -76,7 +76,7 @@ def test_timespan_settings_elements(self): def test_model_domain_elements(self): ''' Check if the domain in the model template has all of the - properties currently in the domain. + properties currently in the domain model. ''' template_keys = sorted(list(self.template['domain'].keys())) domain_path = "client/models/domain.js" diff --git a/stochss/tests/test_settings_template.py b/stochss/tests/test_settings_template.py index a53f417704..40b7ee3a98 100644 --- a/stochss/tests/test_settings_template.py +++ b/stochss/tests/test_settings_template.py @@ -1,33 +1,62 @@ +''' +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 unittest import json import os os.chdir('/stochss') -template_path = "stochss_templates/workflowSettingsTemplate.json" - - class TestWorkflowSettingsTemplate(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for settings template completeness. + ################################################################################################ + ''' + def setUp(self): + ''' + Get the settings template prior to each test. + ''' + template_path = "stochss_templates/workflowSettingsTemplate.json" - def test_settings_elements(self): with open(template_path, "r") as template_file: - template = json.load(template_file) + self.template = json.load(template_file) - template_keys = sorted(list(template.keys())) + def test_workflow_settings_elements(self): + ''' + Check if the settings template has all of the properties currently in the settings model. + ''' + template_keys = sorted(list(self.template.keys())) model_path = "client/models/settings.js" with open(model_path, "r") as model_file: children = model_file.read().split("children: {").pop().split('}')[0].split(',') model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], children))) - - self.assertEqual(template_keys, model_keys) + self.assertEqual(template_keys, model_keys) - def test_simulation_settings_elements(self): - with open(template_path, "r") as template_file: - template = json.load(template_file) - template_keys = sorted(list(template['simulationSettings'].keys())) + def test_workflow_simulation_settings_elements(self): + ''' + Check if the simulation settings in the settings template has + all of the properties currently in the simulation settings model. + ''' + template_keys = sorted(list(self.template['simulationSettings'].keys())) sim_settings_path = "client/models/simulation-settings.js" with open(sim_settings_path, "r") as sim_settings_file: @@ -37,29 +66,32 @@ def test_simulation_settings_elements(self): self.assertEqual(template_keys, sim_settings_keys) - def test_psweep_settings_elements(self): - with open(template_path, "r") as template_file: - template = json.load(template_file) - - template_keys = sorted(list(template['parameterSweepSettings'].keys())) + def test_workflow_parameter_sweep_settings_elements(self): + ''' + Check if the parameter sweep settings in the settings template has + all of the properties currently in the parameter sweep settings model. + ''' + template_keys = sorted(list(self.template['parameterSweepSettings'].keys())) psweep_settings_path = "client/models/parameter-sweep-settings.js" with open(psweep_settings_path, "r") as psweep_settings_file: data = psweep_settings_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') + collections = data.split("collections: {").pop().split('}')[0].split(',') children = data.split("children: {").pop().split('}')[0].split(',') - psweep_settings_keys = list(map(lambda item: item.strip().split(':')[0], props)) - psweep_settings_keys.extend(list(map(lambda item: item.strip().split(':')[0], children))) + psweep_settings_keys = list(map(lambda item: item.strip().split(':')[0], collections)) + psweep_settings_keys.extend(list(map(lambda item: item.strip().split(':')[0], + children))) psweep_settings_keys.sort() self.assertEqual(template_keys, psweep_settings_keys) - def test_results_settings_elements(self): - with open(template_path, "r") as template_file: - template = json.load(template_file) - - template_keys = sorted(list(template['resultsSettings'].keys())) + def test_workflow_results_settings_elements(self): + ''' + Check if the results settings in the settings template has + all of the properties currently in the results settings model. + ''' + template_keys = sorted(list(self.template['resultsSettings'].keys())) results_settings_path = "client/models/results-settings.js" with open(results_settings_path, "r") as results_settings_file: @@ -68,3 +100,17 @@ def test_results_settings_elements(self): self.assertEqual(template_keys, results_settings_keys) + + def test_workflow_timespan_settings_elements(self): + ''' + Check if the timespan settings in the model template has + all of the properties currently in the timespan settings model. + ''' + template_keys = sorted(list(self.template['timespanSettings'].keys())) + tspan_settings_path = "client/models/timespan-settings.js" + + with open(tspan_settings_path, "r") as tspan_settings_file: + data = tspan_settings_file.read().split("props: {").pop().split('}')[0].split(',') + tspan_settings_keys = sorted(list(map(lambda item: item.strip().split(':')[0], data))) + + self.assertEqual(template_keys, tspan_settings_keys) From 59f3bcdd624a18fa244425c2b99d1da6239800aa Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Fri, 25 Jun 2021 13:25:22 -0400 Subject: [PATCH 114/133] Added the test settings template to the test suite. --- stochss/tests/run_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stochss/tests/run_tests.py b/stochss/tests/run_tests.py index ea0b657c89..2b19ca623f 100755 --- a/stochss/tests/run_tests.py +++ b/stochss/tests/run_tests.py @@ -35,7 +35,7 @@ print(os.path.dirname(__file__)) import test_model_template - # import test_settings_template + import test_settings_template # import test_convert_sbml # import test_upload_file # import test_rename @@ -46,7 +46,7 @@ modules = [ test_model_template, - # test_settings_template, + test_settings_template, # test_convert_sbml, # test_rename, # test_upload_file, From 45d2f3bd7fe5724d8e4aa312a6318318b7fbcc2c Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 28 Jun 2021 08:22:35 -0400 Subject: [PATCH 115/133] Fine tuned the tests for StochSSBase. --- stochss/tests/test_stochss_base.py | 140 +++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 stochss/tests/test_stochss_base.py diff --git a/stochss/tests/test_stochss_base.py b/stochss/tests/test_stochss_base.py new file mode 100644 index 0000000000..9353e47c34 --- /dev/null +++ b/stochss/tests/test_stochss_base.py @@ -0,0 +1,140 @@ +''' +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 unittest +# import json +import os +import tempfile + +from pathlib import Path + +from stochss.handlers import StochSSBase + +os.chdir('/stochss') + +class TestStochSSBaseObject(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for the StochSS base class. + ################################################################################################ + ''' + def setUp(self): + ''' + Create a temporary directory with a file and folder for each base test. + ''' + self.tempdir = tempfile.TemporaryDirectory() + test_filename = "test_base_file" + test_foldername = "test_bose_folder" + self.test_filepath = os.path.join(self.tempdir.name, test_filename) + Path(self.test_filepath).touch() + self.test_folderpath = os.path.join(self.tempdir.name, test_foldername) + os.mkdir(self.test_folderpath) + + + def tearDown(self): + ''' + Cleanup the temp directory after each test. + ''' + self.tempdir.cleanup() + + ################################################################################################ + # Unit tests for the StochSS base class check_project_format function. + ################################################################################################ + + def test_check_project_format__old_with_mdl(self): + ''' + Check if the project is identified as old when it contains a model. + ''' + test_files = ["test_model.mdl", "test_model.smdl"] + for test_file in test_files: + with self.subTest(test_file=test_file): + test_file_path = os.path.join(self.test_folderpath, test_file) + Path(test_file_path).touch() + self.assertFalse(StochSSBase.check_project_format(path=self.test_folderpath)) + self.tearDown() + self.setUp() + + + def test_check_project_format__old_with_wkgp1(self): + ''' + Check if the project is identified as old when it contains one + workflow group with the name WorkflowGroup1.wkgp. + ''' + test_wkgp = os.path.join(self.test_folderpath, "WorkflowGroup1.wkgp") + os.mkdir(test_wkgp) + self.assertFalse(StochSSBase.check_project_format(path=self.test_folderpath)) + + + def test_check_project_format__new_empty(self): + ''' + Check if the project is identified as new when empty + ''' + self.assertTrue(StochSSBase.check_project_format(path=self.test_folderpath)) + + + def test_check_project_format__new_with_one_wkgp_not_wkgp1(self): + ''' + Check if the project is identified as new with one workflow group + ''' + test_wkgp = os.path.join(self.test_folderpath, "test_wkgp.wkgp") + os.mkdir(test_wkgp) + self.assertTrue(StochSSBase.check_project_format(path=self.test_folderpath)) + + + def test_check_project_format__new_with_x_wkgps(self): + ''' + Check if the project is identified as new with multiple workflow groups + ''' + test_wkgp1 = os.path.join(self.test_folderpath, "test_wkgp1.wkgp") + os.mkdir(test_wkgp1) + test_wkgp2 = os.path.join(self.test_folderpath, "test_wkgp2.wkgp") + os.mkdir(test_wkgp2) + self.assertTrue(StochSSBase.check_project_format(path=self.test_folderpath)) + + ################################################################################################ + # Unit tests for the StochSS base class check_workflow_format function. + ################################################################################################ + + def test_check_workflow_format__old_with_old_files(self): + ''' + Check if the workflow is identified as old when it an old file. + ''' + test_files = ["info.json", "logs.txt", "RUNNING", "ERROR", "COMPLETE", "test_model.mdl"] + for test_file in test_files: + with self.subTest(test_file=test_file): + test_file_path = os.path.join(self.test_folderpath, test_file) + Path(test_file_path).touch() + self.assertFalse(StochSSBase.check_workflow_format(path=self.test_folderpath)) + self.tearDown() + self.setUp() + + + def test_check_workflow_format__old_with_results(self): + ''' + Check if the workflow is identified as old when it a results folder. + ''' + test_dir = os.path.join(self.test_folderpath, "results") + os.mkdir(test_dir) + self.assertFalse(StochSSBase.check_workflow_format(path=self.test_folderpath)) + + + def test_check_workflow_format__new(self): + ''' + Check if the workflow is identified as old. + ''' + self.assertTrue(StochSSBase.check_workflow_format(path=self.test_folderpath)) From d55d7af1a217c55e372d4dd6378794a289c1e64d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 28 Jun 2021 18:55:07 -0400 Subject: [PATCH 116/133] Made the get_new_path function a classmethod. Fixed bug in get_unique_copy_path. --- stochss/handlers/util/stochss_base.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/stochss/handlers/util/stochss_base.py b/stochss/handlers/util/stochss_base.py index c17075e87f..65a27a6bc0 100644 --- a/stochss/handlers/util/stochss_base.py +++ b/stochss/handlers/util/stochss_base.py @@ -87,7 +87,8 @@ def check_workflow_format(cls, path): return True - def get_new_path(self, dst_path): + @classmethod + def get_new_path(cls, dst_path): ''' Gets the proper destination path for the file object to be moved @@ -96,9 +97,9 @@ def get_new_path(self, dst_path): dst_path : string New path for the file object from the users home directory ''' - new_path = os.path.join(self.user_dir, dst_path) - if dst_path.startswith("trash/") and not "trash" in os.listdir(self.user_dir): - os.mkdir(os.path.join(self.user_dir, "trash")) + new_path = os.path.join(cls.user_dir, dst_path) + if dst_path.startswith("trash/") and not "trash" in os.listdir(cls.user_dir): + os.mkdir(os.path.join(cls.user_dir, "trash")) if new_path.split().pop().replace('.', '', 5).isdigit(): return new_path.replace(new_path.split().pop(), "").strip() if "trash/" in new_path and os.path.exists(new_path): @@ -292,14 +293,16 @@ def get_unique_copy_path(self, path=None): # Check if the file object is an original or at least the second copy if not '-copy' in file or '-copy(' in file: cp_file = ''.join([name, '-copy', ext]) - if cp_file not in os.listdir(dirname if dirname else self.user_dir): + if cp_file not in os.listdir(os.path.join(self.user_dir, dirname) \ + if dirname else self.user_dir): return os.path.join(dirname, cp_file) i = 2 cp_file = ''.join([name, f"-copy({i})", ext]) # Check if a copy exists with '-copy(2)' in the name # If copy_file is still not unique iterate i until a unique name is found - while cp_file in os.listdir(dirname if dirname else self.user_dir): + while cp_file in os.listdir(os.path.join(self.user_dir, dirname) \ + if dirname else self.user_dir): i += 1 cp_file = ''.join([name, f"-copy({i})", ext]) From e01e0b5bc6905f563fd2f74129d1a940a2392246 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 28 Jun 2021 18:56:36 -0400 Subject: [PATCH 117/133] Added tests for StochSSBase functions. --- stochss/tests/run_tests.py | 18 +- stochss/tests/test_stochss_base.py | 781 +++++++++++++++++++++++++++-- 2 files changed, 750 insertions(+), 49 deletions(-) diff --git a/stochss/tests/run_tests.py b/stochss/tests/run_tests.py index 2b19ca623f..e835c00044 100755 --- a/stochss/tests/run_tests.py +++ b/stochss/tests/run_tests.py @@ -36,29 +36,17 @@ import test_model_template import test_settings_template - # import test_convert_sbml - # import test_upload_file - # import test_rename - # import test_generate_zip_file - # import test_workflow_status - # import test_ls - # import test_duplicate + import test_stochss_base modules = [ test_model_template, test_settings_template, - # test_convert_sbml, - # test_rename, - # test_upload_file, - # test_generate_zip_file, - # test_workflow_status, - # test_ls, - # test_duplicate + test_stochss_base ] for module in modules: suite = unittest.TestLoader().loadTestsFromModule(module) - runner = unittest.TextTestRunner(failfast=args.mode == 'staging') + runner = unittest.TextTestRunner(failfast=args.mode == 'staging', verbosity=1) print("Executing: {}".format(module)) result = runner.run(suite) diff --git a/stochss/tests/test_stochss_base.py b/stochss/tests/test_stochss_base.py index 9353e47c34..17cea9c668 100644 --- a/stochss/tests/test_stochss_base.py +++ b/stochss/tests/test_stochss_base.py @@ -16,14 +16,20 @@ along with this program. If not, see . ''' -import unittest -# import json import os +import json +import shutil +import logging +import unittest import tempfile +import datetime +from unittest import mock from pathlib import Path from stochss.handlers import StochSSBase +from stochss.handlers.util.stochss_errors import StochSSFileNotFoundError, FileNotJSONFormatError, \ + StochSSPermissionsError os.chdir('/stochss') @@ -34,12 +40,11 @@ class TestStochSSBaseObject(unittest.TestCase): ################################################################################################ ''' def setUp(self): - ''' - Create a temporary directory with a file and folder for each base test. - ''' + ''' Create a temporary directory with a file and folder for each base test. ''' + self.user_dir = os.path.expanduser("~") self.tempdir = tempfile.TemporaryDirectory() test_filename = "test_base_file" - test_foldername = "test_bose_folder" + test_foldername = "test_base_folder" self.test_filepath = os.path.join(self.tempdir.name, test_filename) Path(self.test_filepath).touch() self.test_folderpath = os.path.join(self.tempdir.name, test_foldername) @@ -47,19 +52,17 @@ def setUp(self): def tearDown(self): - ''' - Cleanup the temp directory after each test. - ''' + ''' Cleanup the temp directory after each test. ''' self.tempdir.cleanup() + if os.path.exists(os.path.join(self.user_dir, "trash")): + shutil.rmtree(os.path.join(self.user_dir, "trash")) ################################################################################################ # Unit tests for the StochSS base class check_project_format function. ################################################################################################ def test_check_project_format__old_with_mdl(self): - ''' - Check if the project is identified as old when it contains a model. - ''' + ''' Check if the project is identified as old when it contains a model. ''' test_files = ["test_model.mdl", "test_model.smdl"] for test_file in test_files: with self.subTest(test_file=test_file): @@ -71,35 +74,27 @@ def test_check_project_format__old_with_mdl(self): def test_check_project_format__old_with_wkgp1(self): - ''' - Check if the project is identified as old when it contains one - workflow group with the name WorkflowGroup1.wkgp. - ''' + ''' Check if the project is identified as old when it contains one + workflow group with the name WorkflowGroup1.wkgp. ''' test_wkgp = os.path.join(self.test_folderpath, "WorkflowGroup1.wkgp") os.mkdir(test_wkgp) self.assertFalse(StochSSBase.check_project_format(path=self.test_folderpath)) def test_check_project_format__new_empty(self): - ''' - Check if the project is identified as new when empty - ''' + ''' Check if the project is identified as new when empty. ''' self.assertTrue(StochSSBase.check_project_format(path=self.test_folderpath)) def test_check_project_format__new_with_one_wkgp_not_wkgp1(self): - ''' - Check if the project is identified as new with one workflow group - ''' + ''' Check if the project is identified as new with one workflow group. ''' test_wkgp = os.path.join(self.test_folderpath, "test_wkgp.wkgp") os.mkdir(test_wkgp) self.assertTrue(StochSSBase.check_project_format(path=self.test_folderpath)) def test_check_project_format__new_with_x_wkgps(self): - ''' - Check if the project is identified as new with multiple workflow groups - ''' + ''' Check if the project is identified as new with multiple workflow groups. ''' test_wkgp1 = os.path.join(self.test_folderpath, "test_wkgp1.wkgp") os.mkdir(test_wkgp1) test_wkgp2 = os.path.join(self.test_folderpath, "test_wkgp2.wkgp") @@ -111,9 +106,7 @@ def test_check_project_format__new_with_x_wkgps(self): ################################################################################################ def test_check_workflow_format__old_with_old_files(self): - ''' - Check if the workflow is identified as old when it an old file. - ''' + ''' Check if the workflow is identified as old when it an old file. ''' test_files = ["info.json", "logs.txt", "RUNNING", "ERROR", "COMPLETE", "test_model.mdl"] for test_file in test_files: with self.subTest(test_file=test_file): @@ -125,16 +118,736 @@ def test_check_workflow_format__old_with_old_files(self): def test_check_workflow_format__old_with_results(self): - ''' - Check if the workflow is identified as old when it a results folder. - ''' + ''' Check if the workflow is identified as old when it a results folder. ''' test_dir = os.path.join(self.test_folderpath, "results") os.mkdir(test_dir) self.assertFalse(StochSSBase.check_workflow_format(path=self.test_folderpath)) def test_check_workflow_format__new(self): - ''' - Check if the workflow is identified as old. - ''' + ''' Check if the workflow is identified as old. ''' self.assertTrue(StochSSBase.check_workflow_format(path=self.test_folderpath)) + + ################################################################################################ + # Unit tests for the StochSS base class get_new_path function. + ################################################################################################ + + def test_get_new_path(self): + ''' Check if the new path is generated correctly. ''' + test_dst_path = os.path.join(self.test_folderpath, "test_file") + test_file_path = StochSSBase.get_new_path(dst_path=test_dst_path) + self.assertEqual(test_file_path, os.path.join(self.user_dir, test_dst_path)) + + + def test_get_new_path__no_trash_folder(self): + ''' Check if the trash directory is created. ''' + test_dst_path = os.path.join("trash", "test_file") + StochSSBase.get_new_path(dst_path=test_dst_path) + self.assertTrue(os.path.exists(os.path.join(self.user_dir, "trash"))) + + + def test_get_new_path__remove_trash_datetime_stamp(self): + ''' Check if the trash datetime stamp is removed from files moving out of trash. ''' + test_dst_path = os.path.join(self.test_folderpath, "trash", "test_file 2021.06.28.08.50.30") + test_file_path = StochSSBase.get_new_path(dst_path=test_dst_path) + expected_path = os.path.join(self.user_dir, os.path.join(self.test_folderpath, "trash", "test_file")) + self.assertEqual(test_file_path, expected_path) + + def test_get_new_path__add_trash_datetime_stamp(self): + ''' Check if the trash datetime stamp is added to duplicate files moved into trash. ''' + os.mkdir(os.path.join(self.test_folderpath, "trash")) + test_dst_path = os.path.join(self.test_folderpath, "trash", "test_file") + Path(test_dst_path).touch() + test_file_path = StochSSBase.get_new_path(dst_path=test_dst_path) + stamp = datetime.datetime.now().strftime(" %y.%m.%d.%H.%M.%S") + expected_path = os.path.join(self.user_dir, os.path.join(self.test_folderpath, "trash", f"test_file{stamp}")) + self.assertEqual(test_file_path, expected_path) + + ################################################################################################ + # Unit tests for the StochSS base class get_file function. + ################################################################################################ + + def test_get_file__no_path_passed(self): + ''' Check if the file from self.path of StochSSBase is returned. ''' + test_base = StochSSBase(path=self.test_folderpath) + self.assertEqual(test_base.get_file(), "test_base_folder") + + + def test_get_file__path_passed(self): + ''' Check is the file from the passed in path is returned. ''' + test_base = StochSSBase(path=self.test_folderpath) + self.assertEqual(test_base.get_file(path=self.test_filepath), "test_base_file") + + ################################################################################################ + # Unit tests for the StochSS base class get_model_template function. + ################################################################################################ + + def test_get_model_template__as_dict(self): + ''' Check if the model template is returned as a dictionary. ''' + test_base = StochSSBase(path=self.test_filepath) + test_template = test_base.get_model_template() + self.assertIsInstance(test_template, dict) + + + def test_get_model_template__as_str(self): + ''' Check is the model template is returned as a string. ''' + test_base = StochSSBase(path=self.test_filepath) + test_template = test_base.get_model_template(as_string=True) + self.assertIsInstance(test_template, str) + + + def test_get_model_template__file_not_found_error(self): + ''' Check if the StochSSFileNotFoundError is raised when the template file is missing. ''' + test_base = StochSSBase(path=self.test_filepath) + with mock.patch("builtins.open", mock.mock_open()) as mock_file: + mock_file.side_effect = FileNotFoundError + with self.assertRaises(StochSSFileNotFoundError): + test_base.get_model_template() + + + def test_get_model_template__json_decoder_error(self): + ''' Check is the FileNotJSONFormatError is raised when the template is not properly formatted. ''' + test_base = StochSSBase(path=self.test_filepath) + with mock.patch("json.load") as mock_load: + mock_load.side_effect = json.decoder.JSONDecodeError(msg="", doc="", pos=1) + with self.assertRaises(FileNotJSONFormatError): + test_base.get_model_template() + + ################################################################################################ + # Unit tests for the StochSS base class get_settings_template function. + ################################################################################################ + + def test_get_settings_template__as_dict(self): + ''' Check if the settings template is returned as a dictionary. ''' + test_base = StochSSBase(path=self.test_filepath) + test_template = test_base.get_settings_template() + self.assertIsInstance(test_template, dict) + + + def test_get_settings_template__as_str(self): + ''' Check is the settings template is returned as a string. ''' + test_base = StochSSBase(path=self.test_filepath) + test_template = test_base.get_settings_template(as_string=True) + self.assertIsInstance(test_template, str) + + + def test_get_settings_template__file_not_found_error(self): + ''' Check if the StochSSFileNotFoundError is raised when the template file is missing. ''' + test_base = StochSSBase(path=self.test_filepath) + with mock.patch("builtins.open", mock.mock_open()) as mock_file: + mock_file.side_effect = FileNotFoundError + with self.assertRaises(StochSSFileNotFoundError): + test_base.get_settings_template() + + + def test_get_settings_template__json_decoder_error(self): + ''' Check is the FileNotJSONFormatError is raised when the template is not properly formatted. ''' + test_base = StochSSBase(path=self.test_filepath) + with mock.patch("json.load") as mock_load: + mock_load.side_effect = json.decoder.JSONDecodeError(msg="", doc="", pos=1) + with self.assertRaises(FileNotJSONFormatError): + test_base.get_settings_template() + + ################################################################################################ + # Unit tests for the StochSS base class get_name function. + ################################################################################################ + + def test_get_name__no_path_passed(self): + ''' Check in the names are return correctly when a path is not passed in. ''' + test_paths = ["test_file", "test_file/", "test_folder/test_file", "test_file.txt"] + for test_path in test_paths: + with self.subTest(test_path=test_path): + test_base = StochSSBase(path=test_path) + self.assertEqual(test_base.get_name(), "test_file") + + + def test_get_name__path_passed(self): + ''' Check in the names are return correctly when a path is passed in. ''' + test_paths = ["test_file", "test_file/", "test_folder/test_file", "test_file.txt"] + test_base = StochSSBase(path=self.test_folderpath) + for test_path in test_paths: + with self.subTest(test_path=test_path): + self.assertEqual(test_base.get_name(path=test_path), "test_file") + + ################################################################################################ + # Unit tests for the StochSS base class get_path function. + ################################################################################################ + + def test_get_path__with_out_user_dir(self): + ''' Check if the path is returned w/o the user directory. ''' + test_base = StochSSBase(path=self.test_filepath) + self.assertEqual(test_base.get_path(), self.test_filepath) + + def test_get_path__with_user_dir(self): + ''' Check if the path is returneded with the user directory. ''' + test_base = StochSSBase(path=self.test_filepath) + self.assertEqual(test_base.get_path(full=True), os.path.join(self.user_dir, self.test_filepath)) + + ################################################################################################ + # Unit tests for the StochSS base class get_dir_name function. + ################################################################################################ + + def test_get_dir_name__with_out_user_dir__root(self): + ''' Check if the dirname is an empty string. ''' + test_base = StochSSBase(path="test_file") + self.assertEqual(test_base.get_dir_name(), "") + + + def test_get_dir_name__with_out_user_dir__not_root(self): + ''' Check if the dirname is returned w/o the user directory. ''' + test_base = StochSSBase(path="test_folder/test_file") + self.assertEqual(test_base.get_dir_name(), "test_folder") + + + def test_get_dir_name__with_user_dir__root(self): + ''' Check if the dirname is the user directory. ''' + test_base = StochSSBase(path="test_file") + self.assertEqual(test_base.get_dir_name(full=True), self.user_dir) + + + def test_get_dir_name__with_user_dir__not_root(self): + ''' Check if the dirname is returned with the user directory. ''' + test_base = StochSSBase(path="test_folder/test_file") + self.assertEqual(test_base.get_dir_name(full=True), os.path.join(self.user_dir, "test_folder")) + + ################################################################################################ + # Unit tests for the StochSS base class get_status function. + ################################################################################################ + + def test_get_status__ready__no_path_passed(self): + ''' Check if ready status is returned when no status file are found and no path is passed in. ''' + test_base = StochSSBase(path=self.test_folderpath) + self.assertEqual(test_base.get_status(), "ready") + + + def test_get_status__ready__path_passed(self): + ''' Check if ready status is returned when no status file are found and a path is passed in. ''' + test_base = StochSSBase(path=self.test_filepath) + self.assertEqual(test_base.get_status(path=self.test_folderpath), "ready") + + + def test_get_status__other__no_path_passed(self): + ''' Check if the status is returned correctly for status files when no path is passed in. ''' + test_files = ["COMPLETE", "ERROR", "RUNNING"] + expected_results = ["complete", "error", "running"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + test_path = os.path.join(self.test_folderpath, test_file) + Path(test_path).touch() + test_base = StochSSBase(path=self.test_folderpath) + self.assertEqual(test_base.get_status(), expected_results[i]) + self.tearDown() + self.setUp() + + + def test_get_status__other__path_passed(self): + ''' Check if the status is returned correctly for status files when no path is passed in. ''' + test_files = ["COMPLETE", "ERROR", "RUNNING"] + expected_results = ["complete", "error", "running"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + test_path = os.path.join(self.test_folderpath, test_file) + Path(test_path).touch() + test_base = StochSSBase(path=self.test_filepath) + self.assertEqual(test_base.get_status(path=self.test_folderpath), expected_results[i]) + self.tearDown() + self.setUp() + + + def test_get_status__file_not_found_error(self): + ''' Check if the StochSSFileNotFoundError is raised when the job is missing. ''' + test_base = StochSSBase(path=self.test_folderpath) + with mock.patch("os.listdir") as mock_folder: + mock_folder.side_effect = FileNotFoundError + with self.assertRaises(StochSSFileNotFoundError): + test_base.get_status() + + + ################################################################################################ + # Unit tests for the StochSS base class get_unique_path function. + ################################################################################################ + + def test_get_unique_path__with_out_dirname__dirname_is_root__unique(self): + ''' Check if you get the correct unique name for a file in root. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(2)", "test_file(2).txt", "test_file(one)", "test_file(one).txt"] + for test_file in test_files: + with self.subTest(test_file=test_file): + test_base = StochSSBase(path="test_base_file") + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertFalse(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, test_file)) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_is_root__one_iter(self): + ''' Check if you get the correct unique name for a file in root when file already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(one)", "test_file(one).txt"] + expected_results = ["test_file(1)", "test_file(1).txt", "test_file(2)", "test_file(2).txt", + "test_file(one)(1)", "test_file(one)(1).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_file)).touch() + test_base = StochSSBase(path="test_base_file") + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_is_root__two_iter(self): + ''' Check if you get the correct unique name for a file in root when a file and iter already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(one)", "test_file(one).txt"] + test_preqs = ["test_file(1)", "test_file(1).txt", "test_file(2)", "test_file(2).txt", + "test_file(one)(1)", "test_file(one)(1).txt"] + expected_results = ["test_file(2)", "test_file(2).txt", "test_file(3)", "test_file(3).txt", + "test_file(one)(2)", "test_file(one)(2).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_file)).touch() + Path(os.path.join(self.test_folderpath, test_preqs[i])).touch() + test_base = StochSSBase(path="test_base_file") + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_is_root__first_iter_removed(self): + ''' Check if you get the correct unique name for a file in root when a iter already exists. ''' + test_files = ["test_file(2)", "test_file(2).txt", "test_file(one)(2)", "test_file(one)(2).txt"] + test_preqs = ["test_file", "test_file.txt", "test_file(one)", "test_file(one).txt"] + expected_results = ["test_file(1)", "test_file(1).txt", "test_file(one)(1)", "test_file(one)(1).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_file)).touch() + Path(os.path.join(self.test_folderpath, test_preqs[1])).touch() + test_base = StochSSBase(path="test_base_file") + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_not_root__unique(self): + ''' Check if you get the correct unique name for a file not in root. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(2)", "test_file(2).txt", "test_file(one)", "test_file(one).txt"] + for test_file in test_files: + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + test_base = StochSSBase(path=os.path.join("test_folder/test_base_file")) + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertFalse(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, "test_folder", test_file)) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_not_root__one_iter(self): + ''' Check if you get the correct unique name for a file not in root when file already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(one)", "test_file(one).txt"] + expected_results = ["test_file(1)", "test_file(1).txt", "test_file(2)", "test_file(2).txt", + "test_file(one)(1)", "test_file(one)(1).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + Path(os.path.join(self.test_folderpath, "test_folder", test_file)).touch() + test_base = StochSSBase(path=os.path.join("test_folder/test_base_file")) + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, "test_folder", expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_not_root__two_iter(self): + ''' Check if you get the correct unique name for a file not in root when a file and iter already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(one)", "test_file(one).txt"] + test_preqs = ["test_file(1)", "test_file(1).txt", "test_file(2)", "test_file(2).txt", + "test_file(one)(1)", "test_file(one)(1).txt"] + expected_results = ["test_file(2)", "test_file(2).txt", "test_file(3)", "test_file(3).txt", + "test_file(one)(2)", "test_file(one)(2).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + Path(os.path.join(self.test_folderpath, "test_folder", test_file)).touch() + Path(os.path.join(self.test_folderpath, "test_folder", test_preqs[i])).touch() + test_base = StochSSBase(path=os.path.join("test_folder/test_base_file")) + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, "test_folder", expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_out_dirname__dirname_not_root__first_iter_removed(self): + ''' Check if you get the correct unique name for a file not in root when a iter already exists. ''' + test_files = ["test_file(2)", "test_file(2).txt", "test_file(one)(2)", "test_file(one)(2).txt"] + test_preqs = ["test_file", "test_file.txt", "test_file(one)", "test_file(one).txt"] + expected_results = ["test_file(1)", "test_file(1).txt", "test_file(one)(1)", "test_file(one)(1).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + Path(os.path.join(self.test_folderpath, "test_folder", test_file)).touch() + Path(os.path.join(self.test_folderpath, "test_folder", test_preqs[1])).touch() + test_base = StochSSBase(path="test_folder/test_base_file") + test_base.user_dir = self.test_folderpath + unique_path, changed = test_base.get_unique_path(name=test_file) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, "test_folder", expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_dirname__unique(self): + ''' Check if you get the correct unique name for a file. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(2)", "test_file(2).txt", "test_file(one)", "test_file(one).txt"] + for test_file in test_files: + with self.subTest(test_file=test_file): + test_base = StochSSBase(path=self.test_filepath) + unique_path, changed = test_base.get_unique_path(name=test_file, + dirname=self.test_folderpath) + self.assertFalse(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, test_file)) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_dirname__one_iter(self): + ''' Check if you get the correct unique name for a file when file already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(one)", "test_file(one).txt"] + expected_results = ["test_file(1)", "test_file(1).txt", "test_file(2)", "test_file(2).txt", + "test_file(one)(1)", "test_file(one)(1).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_file)).touch() + test_base = StochSSBase(path=self.test_filepath) + unique_path, changed = test_base.get_unique_path(name=test_file, + dirname=self.test_folderpath) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_dirname__two_iter(self): + ''' Check if you get the correct unique name for a file when a file and iter already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file(1)", "test_file(1).txt", + "test_file(one)", "test_file(one).txt"] + test_preqs = ["test_file(1)", "test_file(1).txt", "test_file(2)", "test_file(2).txt", + "test_file(one)(1)", "test_file(one)(1).txt"] + expected_results = ["test_file(2)", "test_file(2).txt", "test_file(3)", "test_file(3).txt", + "test_file(one)(2)", "test_file(one)(2).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_file)).touch() + Path(os.path.join(self.test_folderpath, test_preqs[i])).touch() + test_base = StochSSBase(path=self.test_filepath) + unique_path, changed = test_base.get_unique_path(name=test_file, + dirname=self.test_folderpath) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_path__with_dirname__dirname__first_iter_removed(self): + ''' Check if you get the correct unique name for a file in root when a iter already exists. ''' + test_files = ["test_file(2)", "test_file(2).txt", "test_file(one)(2)", "test_file(one)(2).txt"] + test_preqs = ["test_file", "test_file.txt", "test_file(one)", "test_file(one).txt"] + expected_results = ["test_file(1)", "test_file(1).txt", "test_file(one)(1)", "test_file(one)(1).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_file)).touch() + Path(os.path.join(self.test_folderpath, test_preqs[1])).touch() + test_base = StochSSBase(path=self.test_filepath) + unique_path, changed = test_base.get_unique_path(name=test_file, + dirname=self.test_folderpath) + self.assertTrue(changed) + self.assertEqual(unique_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + ################################################################################################ + # Unit tests for the StochSS base class get_unique_copy_path function. + ################################################################################################ + + def test_get_unique_copy_path__with_out_path__dirname_is_root__one_iter(self): + ''' Check if you get the correct copy path for a file in root when path already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file-copy", "test_file-copy.txt"] + expected_results = ["test_file-copy", "test_file-copy.txt", "test_file-copy(2)", "test_file-copy(2).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + test_base = StochSSBase(path=test_file) + test_base.user_dir = self.test_folderpath + unique_copy_path = test_base.get_unique_copy_path() + self.assertEqual(unique_copy_path, expected_results[i]) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_out_path__dirname_is_root__two_iter(self): + ''' Check if you get the correct copy path for a file in root when path and iter already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file-copy", "test_file-copy.txt"] + test_preqs = ["test_file-copy", "test_file-copy.txt", "test_file-copy(2)", "test_file-copy(2).txt"] + expected_results = ["test_file-copy(2)", "test_file-copy(2).txt", "test_file-copy(3)", "test_file-copy(3).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_preqs[i])).touch() + test_base = StochSSBase(path=test_file) + test_base.user_dir = self.test_folderpath + unique_copy_path = test_base.get_unique_copy_path() + self.assertEqual(unique_copy_path, expected_results[i]) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_out_path__dirname_is_root__first_iter_removed(self): + ''' Check if you get the correct copy path for a file in root when a iter already exists. ''' + test_files = ["test_file-copy(2)", "test_file-copy(2).txt"] + expected_results = ["test_file-copy", "test_file-copy.txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + test_base = StochSSBase(path=test_file) + test_base.user_dir = self.test_folderpath + unique_copy_path = test_base.get_unique_copy_path() + self.assertEqual(unique_copy_path, expected_results[i]) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_out_path__dirname_not_root__one_iter(self): + ''' Check if you get the correct copy path for a file not in root when path already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file-copy", "test_file-copy.txt"] + expected_results = ["test_file-copy", "test_file-copy.txt", "test_file-copy(2)", "test_file-copy(2).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + test_base = StochSSBase(path=os.path.join("test_folder", test_file)) + test_base.user_dir = self.test_folderpath + unique_copy_path = test_base.get_unique_copy_path() + self.assertEqual(unique_copy_path, os.path.join("test_folder", expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_out_path__dirname_not_root__two_iter(self): + ''' Check if you get the correct copy path for a file not in root when path and iter already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file-copy", "test_file-copy.txt"] + test_preqs = ["test_file-copy", "test_file-copy.txt", "test_file-copy(2)", "test_file-copy(2).txt"] + expected_results = ["test_file-copy(2)", "test_file-copy(2).txt", "test_file-copy(3)", "test_file-copy(3).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + Path(os.path.join(self.test_folderpath, "test_folder", test_preqs[i])).touch() + test_base = StochSSBase(path=os.path.join("test_folder", test_file)) + test_base.user_dir = self.test_folderpath + unique_copy_path = test_base.get_unique_copy_path() + self.assertEqual(unique_copy_path, os.path.join("test_folder", expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_out_path__dirname_not_root__first_iter_removed(self): + ''' Check if you get the correct copy for a file not in root when a iter already exists. ''' + test_files = ["test_file-copy(2)", "test_file-copy(2).txt"] + expected_results = ["test_file-copy", "test_file-copy.txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + os.mkdir(os.path.join(self.test_folderpath, "test_folder")) + test_base = StochSSBase(path=os.path.join("test_folder", test_file)) + test_base.user_dir = self.test_folderpath + unique_copy_path = test_base.get_unique_copy_path() + self.assertEqual(unique_copy_path, os.path.join("test_folder", expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_path__one_iter(self): + ''' Check if you get the correct copy path for a file when path already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file-copy", "test_file-copy.txt"] + expected_results = ["test_file-copy", "test_file-copy.txt", "test_file-copy(2)", "test_file-copy(2).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + test_base = StochSSBase(path=self.test_filepath) + unique_copy_path = test_base.get_unique_copy_path(path=os.path.join(self.test_folderpath, test_file)) + self.assertEqual(unique_copy_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_path__two_iter(self): + ''' Check if you get the correct copy path for a file when path and iter already exists. ''' + test_files = ["test_file", "test_file.txt", "test_file-copy", "test_file-copy.txt"] + test_preqs = ["test_file-copy", "test_file-copy.txt", "test_file-copy(2)", "test_file-copy(2).txt"] + expected_results = ["test_file-copy(2)", "test_file-copy(2).txt", "test_file-copy(3)", "test_file-copy(3).txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + Path(os.path.join(self.test_folderpath, test_preqs[i])).touch() + test_base = StochSSBase(path=self.test_filepath) + unique_copy_path = test_base.get_unique_copy_path(path=os.path.join(self.test_folderpath, test_file)) + self.assertEqual(unique_copy_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + + def test_get_unique_copy_path__with_path__first_iter_removed(self): + ''' Check if you get the correct copy for a file when a iter already exists. ''' + test_files = ["test_file-copy(2)", "test_file-copy(2).txt"] + expected_results = ["test_file-copy", "test_file-copy.txt"] + for i, test_file in enumerate(test_files): + with self.subTest(test_file=test_file): + test_base = StochSSBase(path=self.test_filepath) + unique_copy_path = test_base.get_unique_copy_path(path=os.path.join(self.test_folderpath, test_file)) + self.assertEqual(unique_copy_path, os.path.join(self.test_folderpath, expected_results[i])) + self.tearDown() + self.setUp() + + ################################################################################################ + # Unit tests for the StochSS base class log function. + ################################################################################################ + + def test_log(self): + ''' Check if log is added correctly ''' + test_base = StochSSBase(path=self.test_filepath) + test_log = {"level": "debug", "message": "testing base log"} + test_base.log("debug", "testing base log") + self.assertIn(test_log, test_base.logs) + + ################################################################################################ + # Unit tests for the StochSS base class make_parent_dirs function. + ################################################################################################ + + def test_make_parent_dirs__one_missing_dir(self): + ''' Check if the parent directories are made correctly. ''' + test_path = os.path.join(self.test_folderpath, "test_folder") + test_base = StochSSBase(path=self.test_folderpath) + with mock.patch('stochss.handlers.util.StochSSBase.get_dir_name') as mock_get_dir_name: + mock_get_dir_name.return_value = test_path + test_base.make_parent_dirs() + self.assertIn("test_folder", os.listdir(self.test_folderpath)) + + + def test_make_parent_dirs__multiple_missing_dir(self): + ''' Check if the parent directories are made correctly. ''' + test_path = os.path.join(self.test_folderpath, "test_folder1", "test_folder2") + test_base = StochSSBase(path=self.test_folderpath) + with mock.patch('stochss.handlers.util.StochSSBase.get_dir_name') as mock_get_dir_name: + mock_get_dir_name.return_value = test_path + test_base.make_parent_dirs() + self.assertIn("test_folder1", os.listdir(self.test_folderpath)) + self.assertIn("test_folder2", os.listdir(os.path.join(self.test_folderpath, "test_folder1"))) + + ################################################################################################ + # Unit tests for the StochSS base class print_logs function. + ################################################################################################ + + def test_print_logs__debug(self): + ''' Check if debug log is logged correctly. ''' + log = logging.getLogger('stochss') + test_base = StochSSBase(path=self.test_folderpath) + test_base.log("debug", "testing debug log") + with mock.patch("stochss.handlers.log.log.debug") as mock_debug_log: + test_base.print_logs(log) + mock_debug_log.assert_called_once_with("testing debug log") + + + def test_print_logs__info(self): + ''' Check if info log is logged correctly. ''' + log = logging.getLogger('stochss') + test_base = StochSSBase(path=self.test_folderpath) + test_base.log("info", "testing info log") + with mock.patch("stochss.handlers.log.log.info") as mock_debug_log: + test_base.print_logs(log) + mock_debug_log.assert_called_once_with("testing info log") + + + def test_print_logs__warning(self): + ''' Check if warning log is logged correctly. ''' + log = logging.getLogger('stochss') + test_base = StochSSBase(path=self.test_folderpath) + test_base.log("warning", "testing warning log") + with mock.patch("stochss.handlers.log.log.warning") as mock_debug_log: + test_base.print_logs(log) + mock_debug_log.assert_called_once_with("testing warning log") + + + def test_print_logs__error(self): + ''' Check if error log is logged correctly. ''' + log = logging.getLogger('stochss') + test_base = StochSSBase(path=self.test_folderpath) + test_base.log("error", "testing error log") + with mock.patch("stochss.handlers.log.log.error") as mock_debug_log: + test_base.print_logs(log) + mock_debug_log.assert_called_once_with("testing error log") + + + def test_print_logs__critical(self): + ''' Check if critical log is logged correctly. ''' + log = logging.getLogger('stochss') + test_base = StochSSBase(path=self.test_folderpath) + test_base.log("critical", "testing critical log") + with mock.patch("stochss.handlers.log.log.critical") as mock_debug_log: + test_base.print_logs(log) + mock_debug_log.assert_called_once_with("testing critical log") + + ################################################################################################ + # Unit tests for the StochSS base class rename function. + ################################################################################################ + + def test_rename__unique_name(self): + ''' Check if file was renamed to the given name. ''' + test_path = os.path.join(self.test_folderpath, "test_file") + Path(test_path).touch() + test_base = StochSSBase(path=test_path) + resp = test_base.rename(name="new_test_file") + self.assertIsInstance(resp, dict) + self.assertFalse(resp['changed']) + self.assertEqual(resp['_path'], os.path.join(self.test_folderpath, "new_test_file")) + + + def test_rename__name_already_exists(self): + ''' Check if file was renamed correctly when name is in use. ''' + test_path = os.path.join(self.test_folderpath, "test_file") + Path(test_path).touch() + test_preq = os.path.join(self.test_folderpath, "new_test_file") + Path(test_preq).touch() + test_base = StochSSBase(path=test_path) + resp = test_base.rename(name="new_test_file") + self.assertIsInstance(resp, dict) + self.assertTrue(resp['changed']) + self.assertEqual(resp['_path'], os.path.join(self.test_folderpath, "new_test_file(1)")) + + + def test_rename__file_not_found_error(self): + ''' Check if the StochSSFileNotFoundError is raised when the file is missing. ''' + test_base = StochSSBase(path="test_file") + with self.assertRaises(StochSSFileNotFoundError): + test_base.rename(name="new_test_file") + + + def test_rename_permission_error(self): + ''' Check if the StochSSPermissionsError is raised when the user doesn't have the correct permissions. ''' + test_base = StochSSBase(path="test_file") + with mock.patch("shutil.move") as mock_move: + mock_move.side_effect = PermissionError + with self.assertRaises(StochSSPermissionsError): + test_base.rename(name="new_test_file") From 4ba9be47124c51b7ad4319f5b680ce06f6b7a083 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 28 Jun 2021 18:58:08 -0400 Subject: [PATCH 118/133] Removed old test files. --- stochss/tests/test_convert_sbml.py | 205 --------------- stochss/tests/test_duplicate.py | 325 ------------------------ stochss/tests/test_generate_zip_file.py | 68 ----- stochss/tests/test_ls.py | 228 ----------------- stochss/tests/test_rename.py | 130 ---------- stochss/tests/test_upload_file.py | 115 --------- stochss/tests/test_workflow_status.py | 52 ---- 7 files changed, 1123 deletions(-) delete mode 100644 stochss/tests/test_convert_sbml.py delete mode 100644 stochss/tests/test_duplicate.py delete mode 100644 stochss/tests/test_generate_zip_file.py delete mode 100644 stochss/tests/test_ls.py delete mode 100644 stochss/tests/test_rename.py delete mode 100644 stochss/tests/test_upload_file.py delete mode 100644 stochss/tests/test_workflow_status.py diff --git a/stochss/tests/test_convert_sbml.py b/stochss/tests/test_convert_sbml.py deleted file mode 100644 index 1c5f940eda..0000000000 --- a/stochss/tests/test_convert_sbml.py +++ /dev/null @@ -1,205 +0,0 @@ -import unittest, sys, os, inspect -import tempfile -from gillespy2 import Model -from gillespy2.solvers.numpy.basic_ode_solver import BasicODESolver - -currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) -parentdir = os.path.dirname(currentdir) -sys.path.insert(0,parentdir) - -from handlers.util.convert_sbml_to_model import * -from handlers.util.stochss_errors import StochSSAPIError, StochSSFileNotFoundError - -class TestConvertSBMLToModel(unittest.TestCase): - - def test_sbml_to_gillespy_success_with_stochss_model(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - self.assertIsInstance(gillespy2_model, Model) - self.assertIsInstance(errors, list) - - - def test_sbml_to_gillespy_convert_error(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test2.sbml" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - self.assertIsNone(gillespy2_model) - self.assertIsInstance(errors, list) - - - def test_sbml_to_gillespy_file_not_found_error(self): - expected = StochSSFileNotFoundError("") - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test3.sbml" - - try: - resp = convert_to_gillespy_model(sbml_file) - self.assertNotIsInstance(resp, tuple) - except StochSSAPIError as err: - self.assertIsInstance(err, StochSSFileNotFoundError) - self.assertEqual(err.status_code, expected.status_code) - self.assertEqual(err.reason, expected.reason) - - - def test_build_stochss_species_from_gillespy_species(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/specie.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], props))) - - species_keys = sorted(list(get_species(gillespy2_model.listOfSpecies, 1)[0][0].keys())) - self.assertEqual(species_keys, model_keys) - - - def test_build_stochss_parameter_from_gillespy_parameter(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/parameter.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], props))) - - parameter_keys = sorted(list(get_parameters(gillespy2_model.listOfParameters, 1)[0][0].keys())) - self.assertEqual(parameter_keys, model_keys) - - - def test_build_stochss_reaction_from_gillespy_reaction(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/reaction.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - collections = data.split("collections: {").pop().split('}')[0].split(',') - children = data.split("children: {").pop().split('}')[0].split(',') - model_keys = list(map(lambda item: item.strip().split(':')[0], props)) - model_keys.extend(list(map(lambda item: item.strip().split(':')[0], collections))) - model_keys.extend(list(map(lambda item: item.strip().split(':')[0], children))) - model_keys.sort() - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - reaction_keys = sorted(list(get_reactions(gillespy2_model.listOfReactions, species, 1)[0][0].keys())) - self.assertEqual(reaction_keys, model_keys) - - - def test_build_stochss_reactant_from_gillespy_reactant(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/stoich-specie.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - children = data.split("children: {").pop().split('}')[0].split(',') - model_keys = list(map(lambda item: item.strip().split(':')[0], props)) - model_keys.extend(list(map(lambda item: item.strip().split(':')[0], children))) - model_keys.sort() - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - reactant_keys = sorted(list(get_reactants(list(gillespy2_model.listOfReactions.popitem(last=False)).pop().reactants, species)[0].keys())) - self.assertEqual(reactant_keys, model_keys) - - - def test_build_stochss_product_from_gillespy_product(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/stoich-specie.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - children = data.split("children: {").pop().split('}')[0].split(',') - model_keys = list(map(lambda item: item.strip().split(':')[0], props)) - model_keys.extend(list(map(lambda item: item.strip().split(':')[0], children))) - model_keys.sort() - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - product_keys = sorted(list(get_products(list(gillespy2_model.listOfReactions.popitem(last=False)).pop().products, species)[0].keys())) - self.assertEqual(product_keys, model_keys) - - - def test_build_stochss_event_from_gillespy_event(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/event.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - collections = data.split("collections: {").pop().split('}')[0].split(',') - model_keys = list(map(lambda item: item.strip().split(':')[0], props)) - model_keys.extend(list(map(lambda item: item.strip().split(':')[0], collections))) - model_keys.sort() - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - parameters = get_parameters(gillespy2_model.listOfParameters, 1)[0] - event_keys = sorted(list(get_events(gillespy2_model.listOfEvents, species, parameters, 1)[0][0].keys())) - self.assertEqual(event_keys, model_keys) - - - def test_build_stochss_event_assignment_from_gillespy_event_assignment(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/event-assignment.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], props))) - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - parameters = get_parameters(gillespy2_model.listOfParameters, 1)[0] - assignment_keys = sorted(list(get_event_assignment(list(gillespy2_model.listOfEvents.popitem(last=False)).pop().assignments, species, parameters)[0].keys())) - self.assertEqual(assignment_keys, model_keys) - - - def test_build_stochss_rate_rule_from_gillespy_rate_rule(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/rule.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], props))) - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - parameters = get_parameters(gillespy2_model.listOfParameters, 1)[0] - rate_rule_keys = sorted(list(get_rate_rules(gillespy2_model.listOfRateRules, species, parameters, 1)[0][0].keys())) - self.assertEqual(rate_rule_keys, model_keys) - - - def test_build_stochss_assignment_rule_from_gillespy_assignment_rule(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/rule.js" - gillespy2_model, errors = convert_to_gillespy_model(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], props))) - - species = get_species(gillespy2_model.listOfSpecies, 1)[0] - parameters = get_parameters(gillespy2_model.listOfParameters, 1)[0] - assignment_rule_keys = sorted(list(get_assignment_rules(gillespy2_model.listOfAssignmentRules, species, parameters, 1)[0][0].keys())) - self.assertEqual(assignment_rule_keys, model_keys) - - - def test_build_stochss_function_definition_from_sbml_function_definition(self): - sbml_file = "stochss/tests/mock_file_sys/sbml_files/test1.sbml" - model_path = "client/models/function-definition.js" - function_definitions = get_sbml_function_definitions(sbml_file) - - with open(model_path, "r") as model_file: - data = model_file.read() - props = data.split("props: {").pop().split('}')[0].split(',') - model_keys = sorted(list(map(lambda item: item.strip().split(':')[0], props))) - - function_definition_keys = sorted(list(get_function_definitions(function_definitions, 1)[0][0].keys())) - self.assertEqual(function_definition_keys, model_keys) - diff --git a/stochss/tests/test_duplicate.py b/stochss/tests/test_duplicate.py deleted file mode 100644 index 80449c7cfa..0000000000 --- a/stochss/tests/test_duplicate.py +++ /dev/null @@ -1,325 +0,0 @@ -import unittest, os, tempfile -from pathlib import Path -from handlers.util.duplicate import * - -class TestDuplicate(unittest.TestCase): - - #unit tests for method get_unique_file_name - - def test_get_unique_file_name_notcopy_noext(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "test_file" - test_filepath = os.path.join(tempdir, test_filename) - assert get_unique_file_name(test_filepath) == os.path.join(tempdir,"test_file-copy") - - def test_get_unique_file_name_iscopy_noext(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "test_file-copy" - test_filepath = os.path.join(tempdir, test_filename) - assert get_unique_file_name(test_filepath) == os.path.join(tempdir, 'test_file-copy(2)') - - def test_get_unique_file_name_notcopy_hasext(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "test_file.sample" - test_filepath = os.path.join(tempdir, test_filename) - assert get_unique_file_name(test_filepath) == os.path.join(tempdir, 'test_file-copy.sample') - - def test_get_unique_file_name_iscopy_hasext(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "test_file-copy.sample" - test_filepath = os.path.join(tempdir, test_filename) - assert get_unique_file_name(test_filepath) == os.path.join(tempdir,"test_file-copy(2).sample") - - def test_get_unique_file_name_multiple_copies(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "test_file" - test_filepath = os.path.join(tempdir, test_filename) - for i in range(2): - test_filepath = get_unique_file_name(test_filepath) - assert test_filepath == os.path.join(tempdir,"test_file-copy(2)") - - #unit tests for method duplicate - - def test_duplicate_file_not_found_raise_error(self): - from handlers.util.stochss_errors import StochSSFileNotFoundError - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"nonexistent_file") - with self.assertRaises(StochSSFileNotFoundError): - duplicate(test_filepath) - - def test_duplicate_file_not_found_no_new_file(self): - from handlers.util.stochss_errors import StochSSFileNotFoundError - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"nonexistent_file") - try: - duplicate(test_filepath) - except StochSSFileNotFoundError: - pass - tempdir_contents = os.listdir(tempdir) - assert len(tempdir_contents) == 0 - - def test_duplicate_permission_not_granted_raise_error(self): - from handlers.util.stochss_errors import StochSSPermissionsError - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"existent_file") - Path(test_filepath).touch() - os.chmod(test_filepath,000) - with self.assertRaises(StochSSPermissionsError): - duplicate(test_filepath) - - def test_duplicate_permission_not_granted_no_new_file(self): - from handlers.util.stochss_errors import StochSSPermissionsError - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"existent_file") - Path(test_filepath).touch() - os.chmod(test_filepath,000) - try: - duplicate(test_filepath) - except StochSSPermissionsError: - pass - tempdir_contents = os.listdir(tempdir) - assert len(tempdir_contents) == 1 - - - def test_duplicate_copy_successful(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"existent_file") - Path(test_filepath).touch() - duplicate(test_filepath) - tempdir_contents = os.listdir(tempdir) - assert os.path.isfile(os.path.join(tempdir,'existent_file-copy')) - - #unit tests for method extract_wkfl_model - - def test_extract_wkfl_model_path_changed(self): - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - Path(test_model_path).touch() - class Test_Workflow: - wkfl_mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl,"wkfl_mdl_path",test_model_path) - test_extract_target="test_model" - extract_wkfl_model(wkfl=test_wkfl,mdl_parent_path=tempdir,model_file=test_extract_target) - assert os.path.isfile(os.path.join(tempdir,"test_model(1)")) - - def test_extract_wkfl_model_path_not_changed(self): - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - Path(test_model_path).touch() - class Test_Workflow: - wkfl_mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl,"wkfl_mdl_path",test_model_path) - test_extract_target="test_model_extracted" - extract_wkfl_model(wkfl=test_wkfl,mdl_parent_path=tempdir,model_file=test_extract_target) - assert os.path.isfile(os.path.join(tempdir,test_extract_target)) - - def test_extract_wkfl_model_file_not_found_raise_error(self): - from handlers.util.stochss_errors import ModelNotFoundError - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - class Test_Workflow: - wkfl_mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl,"wkfl_mdl_path",test_model_path) - with self.assertRaises(ModelNotFoundError): - extract_wkfl_model(wkfl=test_wkfl,mdl_parent_path=tempdir,model_file="test_model") - - def test_extract_wkfl_model_file_not_found_no_new_file(self): - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - class Test_Workflow: - wkfl_mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl,"wkfl_mdl_path",test_model_path) - tempdir_contents = os.listdir(tempdir) - assert len(tempdir_contents) == 0 - - def test_extract_wkfl_model_permission_not_granted_raise_error(self): - from handlers.util.stochss_errors import StochSSPermissionsError - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - Path(test_model_path).touch() - class Test_Workflow: - wkfl_mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl,"wkfl_mdl_path",test_model_path) - os.chmod(test_model_path,000) - with self.assertRaises(StochSSPermissionsError): - extract_wkfl_model(wkfl=test_wkfl,mdl_parent_path=tempdir,model_file="test_model") - - def test_extract_wkfl_model_permission_not_granted_no_new_file(self): - from handlers.util.stochss_errors import StochSSPermissionsError - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - Path(test_model_path).touch() - class Test_Workflow: - wkfl_mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl,"wkfl_mdl_path",test_model_path) - os.chmod(test_model_path,000) - try: - extract_wkfl_model(wkfl=test_wkfl,mdl_parent_path=tempdir,model_file="test_model") - except StochSSPermissionsError: - pass - tempdir_contents = os.listdir(tempdir) - assert len(tempdir_contents) == 1 - - #unit tests for method get_wkfl_model_parent_path - - def test_get_wkfl_model_parent_path_model_only(self): - assert get_wkfl_model_parent_path("test_path", True, "no wkfl") == "test_path" - - def test_get_wkfl_model_parent_path_does_not_exist(self): - with tempfile.TemporaryDirectory() as tempdir: - test_model_path=os.path.join(tempdir,"test_model") - class Test_Workflow: - wkfl_mdl_path = "" - mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl, "",test_model_path) - assert get_wkfl_model_parent_path("path_does_not_exist", False, test_wkfl) == "path_does_not_exist" - - def test_get_wkfl_model_parent_path_exists(self): - with tempfile.TemporaryDirectory() as tempdir: - test_dir_path=os.path.join(tempdir,'test_directory') - os.mkdir(test_dir_path) - test_file_path = os.path.join(test_dir_path,"test_file") - class Test_Workflow: - mdl_path = "" - test_wkfl= Test_Workflow() - setattr(test_wkfl, "mdl_path", test_file_path) - Path(test_file_path).touch() - assert get_wkfl_model_parent_path("path_does_not_exist", False, test_wkfl) == test_dir_path - - #unit tests for method get_model_path - - def test_get_model_path_only_model(self): - assert get_model_path("test_wkfl_parent_path", "test_mdl_parent_path", "test_mdl_file", True) == (os.path.join("test_wkfl_parent_path","test_mdl_file"), "") - - def test_get_model_path_wkfl_parent_path_equals_mdl_parent_path_and_exists(self): - with tempfile.TemporaryDirectory() as tempdir: - test_dual_parent_dir = os.path.join(tempdir, "test_dual_parent_dir") - test_model_path = os.path.join(test_dual_parent_dir,"test_model") - os.mkdir(test_dual_parent_dir) - Path(test_model_path).touch() - assert get_model_path(test_dual_parent_dir, test_dual_parent_dir, "test_model", False) == (os.path.join(test_dual_parent_dir,"test_model"), "") - - def test_get_model_path_mdl_file_in_wkfl_parent_path_directory(self): - with tempfile.TemporaryDirectory() as tempdir: - test_wkfl_parent_dir = os.path.join(tempdir, "test_wkfl_parent_dir") - os.mkdir(test_wkfl_parent_dir) - test_model_parent_dir = os.path.join(tempdir, "test_model_parent_dir") - os.mkdir(test_model_parent_dir) - test_model_path = os.path.join(test_wkfl_parent_dir,"test_model") - Path(test_model_path).touch() - assert get_model_path(test_wkfl_parent_dir, "", "test_model", False) == (os.path.join(test_wkfl_parent_dir,"test_model"), "") - - - def test_get_model_path_mdl_file_in_mdl_parent_path_directory(self): - with tempfile.TemporaryDirectory() as tempdir: - test_wkfl_parent_dir = os.path.join(tempdir, "test_wkfl_parent_dir") - os.mkdir(test_wkfl_parent_dir) - test_model_parent_dir = os.path.join(tempdir, "test_model_parent_dir") - os.mkdir(test_model_parent_dir) - test_model_path = os.path.join(test_model_parent_dir,"test_model") - Path(test_model_path).touch() - assert get_model_path(test_wkfl_parent_dir, test_model_parent_dir, "test_model", False) == (os.path.join(test_model_parent_dir,"test_model"), "") - - - def test_get_model_path_mdl_file_in_mdl_and_wkfl_parent_path_directory(self): - with tempfile.TemporaryDirectory() as tempdir: - test_wkfl_parent_dir = os.path.join(tempdir, "test_wkfl_parent_dir") - os.mkdir(test_wkfl_parent_dir) - test_model_parent_dir = os.path.join(tempdir, "test_model_parent_dir") - os.mkdir(test_model_parent_dir) - test_model_path = os.path.join(test_model_parent_dir,"test_model") - Path(test_model_path).touch() - test_model_path2 = os.path.join(test_wkfl_parent_dir,"test_model") - Path(test_model_path2).touch() - assert get_model_path(test_wkfl_parent_dir, test_model_parent_dir, "test_model", False) == (os.path.join(test_wkfl_parent_dir,"test_model"), "") - - - def test_get_model_path_mdl_file_not_found(self): - with tempfile.TemporaryDirectory() as tempdir: - test_wkfl_parent_dir = os.path.join(tempdir, "test_wkfl_parent_dir") - os.mkdir(test_wkfl_parent_dir) - test_model_parent_dir = os.path.join(tempdir, "test_model_parent_dir") - os.mkdir(test_model_parent_dir) - assert get_model_path(test_wkfl_parent_dir, test_model_parent_dir, "test_model", False) == (os.path.join(test_wkfl_parent_dir,"test_model"), "The model file {0} could not be found. To edit the model or run the workflow you will need to update the path to the model or extract the model from the workflow.".format("test_model")) - - - #unit tests for method duplicate_wkfl_as_new - - def test_duplicate_wkfl_as_new_wkfl_file_not_found(self): - from handlers.util.stochss_errors import StochSSFileNotFoundError - with tempfile.TemporaryDirectory() as tempdir: - with self.assertRaises(StochSSFileNotFoundError): - duplicate_wkfl_as_new(os.path.join(tempdir,"nonexistent_wkfl"), False, "timestamp") - - def test_duplicate_wkfl_as_new_not_JSON_decodable(self): - from handlers.util.stochss_errors import FileNotJSONFormatError - with tempfile.TemporaryDirectory() as tempdir: - Path(os.path.join(tempdir,"info.json")).touch() - with self.assertRaises(FileNotJSONFormatError): - duplicate_wkfl_as_new(os.path.join(tempdir), False, "timestamp") - - - def test_duplicate_wkfl_as_new_only_model(self): - #from json import load - from unittest import mock - from handlers.util.run_model import GillesPy2Workflow - from handlers.util.stochss_errors import FileNotJSONFormatError - with tempfile.TemporaryDirectory() as tempdir: - test_dict={"type":"gillespy","source_model":"test_source_model"} - test_path = os.path.join(tempdir,"test_wkfl_dir") - os.mkdir(test_path) - Path(os.path.join(test_path,"info.json")).touch() - test_source_model_path = os.path.join(test_path,"test_source_model") - Path(test_source_model_path).touch() - with mock.patch("handlers.util.run_model.GillesPy2Workflow.get_settings") as mock_settings: - mock_settings.return_value = None - with mock.patch("json.load") as mock_json: - mock_json.return_value = test_dict - test_return = duplicate_wkfl_as_new(test_path, True, "timestamp") - assert test_return == {'message': 'A copy of the model in {0} has been created'.format(test_path),"mdlPath":os.path.join(tempdir,"test_source_model"),"File":"test_source_model"} - - def test_duplicate_wkfl_as_new_model_file_not_found(self): - #from json import load - from unittest import mock - from handlers.util.run_model import GillesPy2Workflow - from handlers.util.stochss_errors import FileNotJSONFormatError - with tempfile.TemporaryDirectory() as tempdir: - test_dict={"type":"gillespy","source_model":"test_source_model"} - test_path = os.path.join(tempdir,"test_wkfl_dir") - os.mkdir(test_path) - Path(os.path.join(test_path,"info.json")).touch() - test_source_model_path = os.path.join(test_path,"test_source_model") - Path(test_source_model_path).touch() - with mock.patch("handlers.util.run_model.GillesPy2Workflow.get_settings") as mock_settings: - mock_settings.return_value = None - with mock.patch("json.load") as mock_json: - mock_json.return_value = test_dict - test_return = duplicate_wkfl_as_new(test_path, False, "timestamp") - assert test_return == {'message': 'A new workflow has been created from {0}'.format(test_path), 'wkflPath': test_path+"timestamp.wkfl", 'mdlPath': os.path.join(tempdir,"test_source_model"), 'File': 'test_wkfl_dirtimestamp.wkfl', 'mdl_file': 'test_source_model', 'error': 'The model file test_source_model could not be found. To edit the model or run the workflow you will need to update the path to the model or extract the model from the workflow.'} - - def test_duplicate_wkfl_as_new_model_file_exists(self): - #from json import load - from unittest import mock - from handlers.util.run_model import GillesPy2Workflow - from handlers.util.stochss_errors import FileNotJSONFormatError - with tempfile.TemporaryDirectory() as tempdir: - test_dict={"type":"gillespy","source_model":"test_source_model"} - test_path = os.path.join(tempdir,"test_wkfl_dir") - os.mkdir(test_path) - Path(os.path.join(test_path,"info.json")).touch() - test_source_model_path = os.path.join(test_path,"test_source_model") - Path(test_source_model_path).touch() - Path(os.path.join(tempdir,"test_source_model")).touch() - with mock.patch("handlers.util.run_model.GillesPy2Workflow.get_settings") as mock_settings: - mock_settings.return_value = None - with mock.patch("json.load") as mock_json: - mock_json.return_value = test_dict - test_return = duplicate_wkfl_as_new(test_path, False, "timestamp") - assert test_return == {'message': 'A new workflow has been created from {0}'.format(test_path), 'wkflPath': test_path+"timestamp.wkfl", 'mdlPath': os.path.join(tempdir,"test_source_model"), 'File': 'test_wkfl_dirtimestamp.wkfl', 'mdl_file': 'test_source_model'} diff --git a/stochss/tests/test_generate_zip_file.py b/stochss/tests/test_generate_zip_file.py deleted file mode 100644 index 4806ad9173..0000000000 --- a/stochss/tests/test_generate_zip_file.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import unittest -import tempfile -import handlers.util.generate_zip_file as generate_zip_file -import handlers.util.stochss_errors as stochss_errors - -class TestGenerateZipFile(unittest.TestCase): - - #unit tests for method generate_zip_file - - def test_generate_zip_file(self): - test_file = "test_file.foo" - test_path = os.path.join("test_path", test_file) - test_dir = "test_dir" - test_target = "test_target" - with unittest.mock.patch("shutil.make_archive") as mock_make_archive: - generate_zip_file.generate_zip_file(test_path, test_dir, test_target) - correct_filepath = os.path.join(test_dir, "test_file") - mock_make_archive.assert_called_with(correct_filepath, 'zip', test_dir, test_target) - - #unit tests for method get_zip_file_data - - def test_get_zip_file_data(self): - with unittest.mock.patch("builtins.open", unittest.mock.mock_open(read_data="foo")): - assert generate_zip_file.get_zip_file_data("placeholder") == "foo" - - #unit tests for method get_results_csv_dir - - def test_get_results_csv_dir_not_found(self): - test_file = "results_csv" - test_path = "placeholder" - assert generate_zip_file.get_results_csv_dir(test_file, test_path) is None - - def test_get_results_csv_dir_is_found(self): - with tempfile.TemporaryDirectory() as tempdir: - test_dir = os.path.join(tempdir, "results_csv") - os.mkdir(test_dir) - assert generate_zip_file.get_results_csv_dir(test_dir, tempdir) == test_dir - - #unit tests for method download_zip - - def test_download_zip_file_invalid_path(self): - with self.assertRaises(stochss_errors.StochSSFileNotFoundError): - generate_zip_file.download_zip("placeholder_path", "placeholder_action") - - def test_download_zip_file_calls_generate_zip_file(self): - test_path = "test_dir/test_path.foo" - test_action = "generate" - with unittest.mock.patch("os.path.exists"): - with unittest.mock.patch("handlers.util.generate_zip_file.get_unique_file_name",\ - return_value="test_path"): - with unittest.mock.patch("handlers.util.generate_zip_file.generate_zip_file")\ - as mock_generate: - generate_zip_file.download_zip(test_path, test_action) - mock_generate.assert_called_with("t", os.path.join("/home/jovyan", "test_dir"), \ - os.path.join("/home/jovyan", test_path)) - - def test_download_zip_file_action_generate(self): - test_path = "test_dir/test_path.foo" - test_action = "generate" - test_resp = {"Message":"Successfully created t", "Path":"t",} - with unittest.mock.patch("os.path.exists"): - with unittest.mock.patch("handlers.util.generate_zip_file.get_unique_file_name",\ - return_value="test_path"): - with unittest.mock.patch("handlers.util.generate_zip_file.generate_zip_file"): - assert generate_zip_file.download_zip(test_path, test_action) == test_resp - - #download_zip action resultscsv is not covered pending refactor of user dir from /home/jovyan diff --git a/stochss/tests/test_ls.py b/stochss/tests/test_ls.py deleted file mode 100644 index e5d6f3b7e9..0000000000 --- a/stochss/tests/test_ls.py +++ /dev/null @@ -1,228 +0,0 @@ -"""Unit tests for handlers.util.ls.py""" -import unittest -import os -import tempfile -from pathlib import Path - -import handlers.util.ls as ls -from handlers.util.stochss_errors import StochSSFileNotFoundError - -class TestLS(unittest.TestCase): - """Unit test container for handlers.util.ls.py""" - - #unit tests for method get_file_system_data - - def test_get_file_system_data_dir_not_found(self): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "nonexistent_dir") - with self.assertRaises(StochSSFileNotFoundError): - ls.get_file_system_data(test_path, tempdir) - - @classmethod - def test_get_file_system_data_no_children(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "empty_dir") - os.mkdir(test_path) - children = ls.get_file_system_data(test_path, tempdir) - assert len(children) == 0 - - @classmethod - def test_get_file_system_data_child_is_wkfl(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.wkfl") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with \ - (text="test_file.wkfl", f_type="workflow", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_mdl(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.mdl") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_file.mdl", f_type="nonspatial", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_smdl(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.smdl") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_file.smdl", f_type="spatial", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_mesh(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.mesh") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_file.mesh", f_type="mesh", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_ipynb(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.ipynb") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_file.ipynb", f_type="notebook", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_sbml(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.sbml") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_file.sbml", f_type="sbml-model", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_dir(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_dir_path = os.path.join(test_path, "test_dir") - os.mkdir(test_dir_path) - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_dir", f_type="folder", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_other(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_file_path = os.path.join(test_path, "test_file.foo") - Path(test_file_path).touch() - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_file.foo", f_type="other", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_exp(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_dir_path = os.path.join(test_path, "test_dir.wkgp") - os.mkdir(test_dir_path) - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_dir.wkgp", f_type="workflow-group", p_path=tempdir) - - @classmethod - def test_get_file_system_data_child_is_proj(cls): - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "parent_dir") - os.mkdir(test_path) - test_dir_path = os.path.join(test_path, "test_dir.proj") - os.mkdir(test_dir_path) - with unittest.mock.patch("handlers.util.ls.build_child") as mock_build_child: - ls.get_file_system_data(test_path, tempdir) - mock_build_child.assert_called_once_with\ - (text="test_dir.proj", f_type="project", p_path=tempdir) - - #unit tests for method build_child - - @classmethod - def test_build_child_top_level(cls): - with tempfile.TemporaryDirectory(): - test_p_path = "none" - test_text = "test_text" - test_f_type = "test_f_type" - assert ls.build_child(text=test_text, f_type=test_f_type, p_path=test_p_path) ==\ - {"text" : "test_text", "type" : "test_f_type", \ - "_path" : "test_text", "children" : False} - - @classmethod - def test_build_child_sub_level(cls): - with tempfile.TemporaryDirectory(): - test_p_path = "test_p_path" - test_text = "test_text" - test_f_type = "test_f_type" - assert ls.build_child(text=test_text, f_type=test_f_type, p_path=test_p_path) == \ - {"text" : "test_text", "type" : "test_f_type", \ - "_path" : os.path.join(test_p_path, test_text), "children" : False} - - @classmethod - def test_build_child_wkfl_calls_get_status(cls): - with tempfile.TemporaryDirectory(): - test_p_path = "test_p_path" - test_text = "test_text" - test_f_type = "workflow" - with unittest.mock.patch("handlers.util.ls.get_status") as mock_get_status: - ls.build_child(text=test_text, p_path=test_p_path, f_type=test_f_type) - mock_get_status.assert_called() - - #unit tests for method build_root - - @classmethod - def test_build_root(cls): - test_children = "test_children" - assert ls.build_root(test_children) ==\ - [{"text":"/", "type":"root", "_path":"/", \ - "children":"test_children", "state":{"opened":True}}] - - #unit tests for method check_extension - - @classmethod - def test_check_extension_true(cls): - test_str = "test" - test_target = "target" - test_name = test_str + test_target - assert ls.check_extension(test_name, test_target) - - @classmethod - def test_check_extension_false(cls): - test_str = "test" - test_target = "target" - test_name = test_str - assert not ls.check_extension(test_name, test_target) - - #unit tests for method list_files - - @classmethod - def test_ls_p_path_none(cls): - test_p_path = "none" - with unittest.mock.patch("handlers.util.ls.build_root") as mock_build_root: - with unittest.mock.patch("handlers.util.ls.get_file_system_data", \ - return_value="test_return") as mock_get_file_system_data: - with unittest.mock.patch("json.dumps"): - ls.list_files(p_path=test_p_path) - mock_get_file_system_data.assert_called() - mock_build_root.assert_called_with("test_return") - - @classmethod - def test_ls_p_path_not_none(cls): - test_p_path = "test_p_path" - test_full_path = os.path.join("/home/jovyan", test_p_path) - with unittest.mock.patch("handlers.util.ls.build_root"): - with unittest.mock.patch("handlers.util.ls.get_file_system_data", \ - return_value="test_return") as mock_get_file_system_data: - with unittest.mock.patch("json.dumps"): - ls.list_files(p_path=test_p_path) - mock_get_file_system_data.assert_called_with(test_full_path, test_p_path) diff --git a/stochss/tests/test_rename.py b/stochss/tests/test_rename.py deleted file mode 100644 index 7940647080..0000000000 --- a/stochss/tests/test_rename.py +++ /dev/null @@ -1,130 +0,0 @@ -import unittest, os, tempfile -from pathlib import Path -from handlers.util.rename import * - -class TestRename(unittest.TestCase): - - #unit tests for method get_unique_file_name - - def test_get_unique_file_name_target_does_not_exist(self): - with tempfile.TemporaryDirectory() as tempdir: - gufn_return_filepath = os.path.join(tempdir,"nonexistent_file") - gufn_return_tuple = (gufn_return_filepath, False) - assert get_unique_file_name("nonexistent_file", tempdir) == (gufn_return_tuple) - - def test_get_unique_file_name_target_exists(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"existent_file") - Path(test_filepath).touch() - gufn_return_filepath = os.path.join(tempdir,"existent_file(1)") - gufn_return_tuple = (gufn_return_filepath, True) - assert get_unique_file_name("existent_file", tempdir) == gufn_return_tuple - - def test_get_unique_file_name_numbered_target_exists(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filepath = os.path.join(tempdir,"existent_file(1)") - Path(test_filepath).touch() - gufn_return_filepath = os.path.join(tempdir,"existent_file(2)") - gufn_return_tuple = (gufn_return_filepath, True) - assert get_unique_file_name("existent_file(1)", tempdir) == gufn_return_tuple - - def test_get_unique_file_name_with_extension_target_does_not_exist(self): - with tempfile.TemporaryDirectory() as tempdir: - gufn_return_filepath = os.path.join(tempdir,"nonexistent_file.foo") - gufn_return_tuple = (gufn_return_filepath, False) - assert get_unique_file_name("nonexistent_file.foo", tempdir) == (gufn_return_tuple) - - def test_get_unique_file_name_with_extension_target_exists(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "existent_file.foo" - test_filepath = os.path.join(tempdir,test_filename) - Path(test_filepath).touch() - gufn_return_filepath = os.path.join(tempdir,"existent_file(1).foo") - gufn_return_tuple = (gufn_return_filepath, True) - assert get_unique_file_name(test_filename, tempdir) == gufn_return_tuple - - - def test_get_unique_file_name_iterated(self): - with tempfile.TemporaryDirectory() as tempdir: - test_filename = "existent_file" - test_filepath = os.path.join(tempdir, test_filename) - Path(test_filepath).touch() - for i in range(3): - test_filename, placeholder = get_unique_file_name(test_filename, tempdir) - Path(test_filename).touch() - test_filename = test_filename.split('/').pop() - assert len(os.listdir(tempdir)) == 4 - - def test_get_unique_file_name_invalid_path(self): - with tempfile.TemporaryDirectory() as tempdir: - test_dirpath = os.path.join(tempdir,"invalid_directory") - with self.assertRaises(FileNotFoundError): - get_unique_file_name("nonexistent_file", test_dirpath) - - #unit tests for method rename - - def test_rename_file(self): - with tempfile.TemporaryDirectory() as tempdir: - test_file = "test_file" - test_path = os.path.join(tempdir,test_file) - Path(test_path).touch() - rename(test_path, "test_file2") - test_new_path = os.path.join(tempdir, "test_file2") - assert os.path.isfile(test_new_path) - - def test_rename_directory(self): - with tempfile.TemporaryDirectory() as tempdir: - test_dir = "test_dir" - test_path = os.path.join(tempdir, test_dir) - os.mkdir(test_path) - rename(test_path, "test_dir2") - test_new_path = os.path.join(tempdir, "test_dir2") - assert os.path.isdir(test_new_path) - - def test_rename_permission_error(self): - from handlers.util.stochss_errors import StochSSPermissionsError - from unittest import mock - with tempfile.TemporaryDirectory() as tempdir: - test_dir = "test_dir" - test_file = "test_file" - test_dir_path = os.path.join(tempdir,test_dir) - os.mkdir(test_dir_path) - test_path = os.path.join(test_dir_path,test_file) - Path(test_path).touch() - with mock.patch("shutil.move", side_effect=StochSSPermissionsError('test_error')) as mock_shutil: - with self.assertRaises(StochSSPermissionsError): - rename(test_path, "test_file2") - - def test_rename_file_not_found_error(self): - from handlers.util.stochss_errors import StochSSFileNotFoundError - with tempfile.TemporaryDirectory() as tempdir: - test_file = "test_file" - test_path = os.path.join(tempdir,test_file) - with self.assertRaises(StochSSFileNotFoundError): - rename(test_path, "test_file2") - - def test_rename_target_exists(self): - with tempfile.TemporaryDirectory() as tempdir: - test_file = "test_file" - test_path = os.path.join(tempdir, test_file) - Path(test_path).touch() - test_file2 = "test_file2" - test_path2 = os.path.join(tempdir, test_file2) - Path(test_path2).touch() - resp = rename(test_path, "test_file2") - assert resp["changed"] == True - - def test_rename_running_wkfl(self): - from unittest import mock - with tempfile.TemporaryDirectory() as tempdir: - with mock.patch("json.dump") as mock_dump: - with mock.patch("json.load") as mock_json: - with mock.patch('builtins.open', mock.mock_open(read_data="foo")) as m: - test_file = "test_file.wkfl" - test_path = os.path.join(tempdir, test_file) - target_path = os.path.join(tempdir, "test_file2") - os.mkdir(test_path) - os.mkdir(target_path) - Path(os.path.join(target_path,"RUNNING")).touch() - rename(test_path, "test_file2") - m.assert_called_with(os.path.join(target_path, "info.json"), 'r+') diff --git a/stochss/tests/test_upload_file.py b/stochss/tests/test_upload_file.py deleted file mode 100644 index 404c863166..0000000000 --- a/stochss/tests/test_upload_file.py +++ /dev/null @@ -1,115 +0,0 @@ -import os -import unittest -from unittest import mock -import tempfile -from pathlib import Path - -from handlers.util.upload_file import * - -class TestUploadFile(unittest.TestCase): - - #unit tests for method validate_model - - def test_validate_model_json_decode_error(self): - with tempfile.TemporaryDirectory() as tempdir: - test_file = "non_json_file" - test_path = os.path.join(tempdir, test_file) - Path(test_path).touch() - assert validate_model(test_path, test_file) == \ - (False, False, "The file {0} is not in JSON format.".format(test_file)) - - def test_validate_model_valid_keys(self): - test_keys = ("is_spatial", "defaultID", "defaultMode", "modelSettings", - "simulationSettings", "parameterSweepSettings", "species", - "parameters", "reactions", "eventsCollection", "rules", - "functionDefinitions", "meshSettings", "initialConditions") - class TestKeychain(): - def keys(self): - return test_keys - - complete_keychain = TestKeychain() - with mock.patch("json.loads", return_value=complete_keychain): - assert validate_model("", "") == (True, True, "") - - def test_validate_model_missing_keys(self): - test_keys = ("species","parameters","reactions","eventsCollection", - "rules","functionDefinitions") - - class TestKeychain(): - def keys(self): - return () - - complete_keychain = TestKeychain() - with mock.patch("json.loads", return_value=complete_keychain): - assert validate_model("", "") == (False, True, \ - "The following keys are missing from {0}: {1}".format("", ", ".join(test_keys))) - - #unit tests for method upload_model_file - - def test_upload_model_file_is_valid(self): - test_valid = True - test_json = False - test_error = "" - predicted_name = ".".join(["some_name", "mdl"]) - with mock.patch("handlers.util.upload_file.get_unique_file_name",\ - return_value=("some_path", False)): - with mock.patch("handlers.util.upload_file.validate_model",\ - return_value=(test_valid, test_json, test_error)): - with mock.patch("builtins.open", mock.mock_open(read_data="foo")): - assert upload_model_file("some_path", "some_file_name", "some_name", None)\ - ['message'] == "{0} was successfully uploaded to {1}".format(\ - predicted_name, "some_path") - - def test_upload_model_file_not_valid(self): - test_valid = False - test_json = False - test_error = "" - predicted_name = ".".join(["some_name", "json"]) - with mock.patch("handlers.util.upload_file.get_unique_file_name",\ - return_value=("some_path", False)): - with mock.patch("handlers.util.upload_file.validate_model",\ - return_value=(test_valid, test_json, test_error)): - with mock.patch("builtins.open", mock.mock_open(read_data="foo")): - assert upload_model_file("some_path",\ - "some_file_name", "some_name", None)['message'] ==\ - "{0} could not be validated as a Model file and was uploaded as {1} to {2}"\ - .format("some_file_name", predicted_name, "some_path") - - def test_upload_model_file_error(self): - test_valid = False - test_json = False - test_error = "test_error" - with mock.patch("handlers.util.upload_file.get_unique_file_name",\ - return_value=("some_path", False)): - with mock.patch("handlers.util.upload_file.validate_model", return_value=\ - (test_valid, test_json, test_error)): - with mock.patch("builtins.open", mock.mock_open(read_data="foo")): - assert upload_model_file("some_path", "some_file_name", "some_name", None)\ - ['errors'] == ['test_error'] - - def test_upload_model_file_name_changed(self): - test_valid = False - test_json = False - test_error = "" - predicted_name = "some_file" - with mock.patch("handlers.util.upload_file.get_unique_file_name",\ - return_value=(os.path.join("some_parent", "some_file"), True)): - with mock.patch("handlers.util.upload_file.validate_model", \ - return_value=(test_valid, test_json, test_error)): - with mock.patch("builtins.open", mock.mock_open(read_data="foo")): - assert upload_model_file("some_path", "some_file_name", "some_name", None)['message'] ==\ - "{0} could not be validated as a Model file and was uploaded as {1} to {2}"\ - .format("some_file_name", predicted_name, "some_path") - - def test_upload_model_file_json(self): - test_valid = True - test_json = True - test_error = "" - with mock.patch("handlers.util.upload_file.get_unique_file_name",\ - return_value=("some_path", False)): - with mock.patch("handlers.util.upload_file.validate_model", return_value=\ - (test_valid, test_json, test_error)): - with mock.patch("builtins.open", mock.mock_open(read_data="foo")): - with mock.patch("json.loads", return_value="mock_return") as mock_json_loads: - upload_model_file("some_path", "some_file_name", "some_name", None) - mock_json_loads.assert_called() diff --git a/stochss/tests/test_workflow_status.py b/stochss/tests/test_workflow_status.py deleted file mode 100644 index 36e890c31e..0000000000 --- a/stochss/tests/test_workflow_status.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -import unittest -import tempfile -from pathlib import Path - -from handlers.util.workflow_status import get_status - -class TestWorkflow_Status(unittest.TestCase): - - #unit tests for method get_status - - def test_get_status_complete(self): - from unittest import mock - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "test_dir") - os.mkdir(test_path) - test_status = "COMPLETE" - test_file = os.path.join(test_path, test_status) - Path(test_file).touch() - with mock.patch("os.path.join",return_value = test_path) as mock_join: - assert get_status("") == "complete" - - - def test_get_status_error(self): - from unittest import mock - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "test_dir") - os.mkdir(test_path) - test_status = "ERROR" - test_file = os.path.join(test_path, test_status) - Path(test_file).touch() - with mock.patch("os.path.join",return_value = test_path) as mock_join: - assert get_status("") == "error" - - def test_get_status_running(self): - from unittest import mock - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "test_dir") - os.mkdir(test_path) - test_status = "RUNNING" - test_file = os.path.join(test_path, test_status) - Path(test_file).touch() - with mock.patch("os.path.join",return_value = test_path) as mock_join: - assert get_status("") == "running" - - def test_get_status_ready(self): - from unittest import mock - with tempfile.TemporaryDirectory() as tempdir: - test_path = os.path.join(tempdir, "test_dir") - os.mkdir(test_path) - with mock.patch("os.path.join",return_value = test_path) as mock_join: - assert get_status("") == "ready" From accaf186fca7db4d3ecfb927d143ee8ff0b3b10e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 08:59:25 -0400 Subject: [PATCH 119/133] Fixed test. --- stochss/tests/test_stochss_base.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stochss/tests/test_stochss_base.py b/stochss/tests/test_stochss_base.py index 17cea9c668..d4f0bbee7e 100644 --- a/stochss/tests/test_stochss_base.py +++ b/stochss/tests/test_stochss_base.py @@ -54,8 +54,8 @@ def setUp(self): def tearDown(self): ''' Cleanup the temp directory after each test. ''' self.tempdir.cleanup() - if os.path.exists(os.path.join(self.user_dir, "trash")): - shutil.rmtree(os.path.join(self.user_dir, "trash")) + if StochSSBase.user_dir != os.path.expanduser("~"): + StochSSBase.user_dir = os.path.expanduser("~") ################################################################################################ # Unit tests for the StochSS base class check_project_format function. @@ -142,8 +142,10 @@ def test_get_new_path(self): def test_get_new_path__no_trash_folder(self): ''' Check if the trash directory is created. ''' test_dst_path = os.path.join("trash", "test_file") + StochSSBase.user_dir = self.test_folderpath StochSSBase.get_new_path(dst_path=test_dst_path) - self.assertTrue(os.path.exists(os.path.join(self.user_dir, "trash"))) + self.assertTrue(os.path.exists(os.path.join(self.test_folderpath, "trash"))) + def test_get_new_path__remove_trash_datetime_stamp(self): From e9c2610e3b881244d4fd9d8e1e5c5c50962e9568 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 09:52:32 -0400 Subject: [PATCH 120/133] Refactored the timestep size slider to use log scale values from 1e-5 to 1. --- client/templates/includes/timespanSettings.pug | 16 ++++++++-------- client/views/timespan-settings.js | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/client/templates/includes/timespanSettings.pug b/client/templates/includes/timespanSettings.pug index 34401ab4e3..e49fe14c96 100644 --- a/client/templates/includes/timespanSettings.pug +++ b/client/templates/includes/timespanSettings.pug @@ -59,9 +59,9 @@ div#preview-settings.card div.row - div.col-sm-3 + div.col-sm-1 - span.inline 1e-5 + span.inline 1 div.col-sm-8 @@ -69,16 +69,16 @@ div#preview-settings.card div.inline(data-hook="timestep-size-value") - div.col-sm-1.d-flex.justify-content-end + div.col-sm-3.d-flex.justify-content-end - span.inline 1 + span.inline 1e-5 input.custom-range( type="range" - min="1e-5" - max="1" - step="1e-5" - value=this.model.timestepSize + min="0" + max="5" + step="1" + value=this.tssValue data-hook="timestep-size-slider" ) diff --git a/client/views/timespan-settings.js b/client/views/timespan-settings.js index fa5f87cb4b..47ae7d6446 100644 --- a/client/views/timespan-settings.js +++ b/client/views/timespan-settings.js @@ -40,6 +40,8 @@ module.exports = View.extend({ View.prototype.initialize.apply(this, arguments); this.readOnly = attrs.readOnly ? attrs.readOnly : false; this.tooltips = Tooltips.modelSettings + let tssValues = {1e-5: 5, 1e-4: 4, 1e-3: 3, 1e-2: 2, 1e-1: 1, 1: 0} + this.tssValue = tssValues[this.model.timestepSize] }, render: function () { View.prototype.render.apply(this, arguments); @@ -64,7 +66,7 @@ module.exports = View.extend({ app.changeCollapseButtonText(this, e); }, setTimestepSize: function (e) { - this.model.timestepSize = Number(e.target.value); + this.model.timestepSize = Number("1e-" + e.target.value); $(this.queryByHook("view-timestep-size")).html(this.model.timestepSize); }, updateViewer: function (e) { @@ -72,7 +74,7 @@ module.exports = View.extend({ $(this.queryByHook("view-time-step")).html(this.model.timeStep); }, viewTimestepValue: function (e) { - let value = e.target.value; + let value = Number("1e-" + e.target.value); $(this.queryByHook("timestep-size-value")).html(value); }, subviews: { From bc8c80ccad7074960d795d9b4331a12b048cc1a0 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 09:59:05 -0400 Subject: [PATCH 121/133] Refactored the load and model convert to spatial function to use ie-5 for the timestep size. --- stochss/handlers/util/stochss_model.py | 2 +- stochss/handlers/util/stochss_spatial_model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stochss/handlers/util/stochss_model.py b/stochss/handlers/util/stochss_model.py index 6b617b2385..95d3be5e28 100644 --- a/stochss/handlers/util/stochss_model.py +++ b/stochss/handlers/util/stochss_model.py @@ -381,7 +381,7 @@ def convert_to_spatial(self): model = self.load() model['is_spatial'] = True if "timestepSize" not in self.model['modelSettings'].keys(): - self.model['modelSettings']['timestepSize'] = self.model['modelSettings']['timeStep'] + self.model['modelSettings']['timestepSize'] = 1e-5 if "domain" not in model.keys(): model['domain'] = self.get_model_template()['domain'] for species in model['species']: diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index 99f607d217..13602e7c28 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -572,7 +572,7 @@ def load(self): if not self.model['defaultMode']: self.model['defaultMode'] = "discrete" if "timestepSize" not in self.model['modelSettings'].keys(): - self.model['modelSettings']['timestepSize'] = self.model['modelSettings']['timeStep'] + self.model['modelSettings']['timestepSize'] = 1e-5 if "domain" not in self.model.keys() or len(self.model['domain'].keys()) < 6: self.model['domain'] = self.get_model_template()['domain'] elif "static" not in self.model['domain'].keys(): From 460dc626fc1dc5739a394410b3fd13a8b2a04d13 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 12:50:53 -0400 Subject: [PATCH 122/133] Updated copyright to 2021. --- COPYRIGHT | 2 +- client/app.js | 2 +- client/config.js | 2 +- client/graphics.js | 2 +- client/modals.js | 2 +- client/models/boundary-condition.js | 2 +- client/models/boundary-conditions.js | 2 +- client/models/creator.js | 2 +- client/models/creators.js | 2 +- client/models/domain-type.js | 2 +- client/models/domain-types.js | 2 +- client/models/domain.js | 2 +- client/models/event-assignment.js | 2 +- client/models/event-assignments.js | 2 +- client/models/event.js | 2 +- client/models/events.js | 2 +- client/models/function-definition.js | 2 +- client/models/function-definitions.js | 2 +- client/models/initial-condition.js | 2 +- client/models/initial-conditions.js | 2 +- client/models/job.js | 2 +- client/models/jobs.js | 2 +- client/models/me.js | 2 +- client/models/metadata.js | 2 +- client/models/model.js | 2 +- client/models/parameter-sweep-settings.js | 2 +- client/models/parameter.js | 2 +- client/models/parameters.js | 2 +- client/models/particle.js | 2 +- client/models/particles.js | 2 +- client/models/project.js | 2 +- client/models/reaction.js | 2 +- client/models/reactions.js | 2 +- client/models/results-settings.js | 2 +- client/models/rule.js | 2 +- client/models/rules.js | 2 +- client/models/settings.js | 2 +- client/models/simulation-settings.js | 2 +- client/models/specie.js | 2 +- client/models/species.js | 2 +- client/models/stoich-specie.js | 2 +- client/models/stoich-species.js | 2 +- client/models/sweep-parameter.js | 2 +- client/models/sweep-parameters.js | 2 +- client/models/timespan-settings.js | 2 +- client/models/workflow-group.js | 2 +- client/models/workflow-groups.js | 2 +- client/models/workflow.js | 2 +- client/models/workflows.js | 2 +- client/page-help.js | 2 +- client/pages/base.js | 2 +- client/pages/domain-editor.js | 2 +- client/pages/file-browser.js | 2 +- client/pages/home.js | 2 +- client/pages/loading-page.js | 2 +- client/pages/model-editor.js | 2 +- client/pages/multiple-plots.js | 2 +- client/pages/page.js | 2 +- client/pages/project-browser.js | 2 +- client/pages/project-manager.js | 2 +- client/pages/quickstart.js | 2 +- client/pages/users-home.js | 2 +- client/pages/workflow-manager.js | 2 +- client/pages/workflow-selection.js | 2 +- client/reaction-types.js | 2 +- client/styles/styles.css | 2 +- client/tooltips.js | 2 +- client/views/archive-listing.js | 2 +- client/views/boundary-condition-view.js | 2 +- client/views/boundary-conditions-editor.js | 2 +- client/views/component-types.js | 2 +- client/views/creator-listing.js | 2 +- client/views/domain-viewer.js | 2 +- client/views/edit-3D-domain.js | 2 +- client/views/edit-custom-stoich-specie.js | 2 +- client/views/edit-domain-type.js | 2 +- client/views/edit-event-assignment.js | 2 +- client/views/edit-function-definition.js | 2 +- client/views/edit-initial-condition.js | 2 +- client/views/edit-parameter.js | 2 +- client/views/edit-particle.js | 2 +- client/views/edit-project.js | 2 +- client/views/edit-rule.js | 2 +- client/views/edit-species.js | 2 +- client/views/edit-stoich-specie.js | 2 +- client/views/event-assignments-editor.js | 2 +- client/views/event-details.js | 2 +- client/views/event-listings.js | 2 +- client/views/events-editor.js | 2 +- client/views/file-browser-view.js | 2 +- client/views/initial-conditions-editor.js | 2 +- client/views/initial-conditions-viewer.js | 2 +- client/views/input.js | 2 +- client/views/job-listing.js | 2 +- client/views/main.js | 2 +- client/views/mesh-editor.js | 2 +- client/views/meta-data.js | 2 +- client/views/model-listing.js | 2 +- client/views/model-state-buttons.js | 2 +- client/views/model-viewer.js | 2 +- client/views/parameter-settings.js | 2 +- client/views/parameters-editor.js | 2 +- client/views/quickview-domain-types.js | 2 +- client/views/reactant-product.js | 2 +- client/views/reaction-details.js | 2 +- client/views/reaction-listing.js | 2 +- client/views/reaction-types.js | 2 +- client/views/reactions-editor.js | 2 +- client/views/rules-editor.js | 2 +- client/views/sbml-component-editor.js | 2 +- client/views/settings-viewer.js | 2 +- client/views/settings.js | 2 +- client/views/simulation-settings.js | 2 +- client/views/species-editor.js | 2 +- client/views/sweep-parameter-range.js | 2 +- client/views/sweep-parameter.js | 2 +- client/views/tests.js | 2 +- client/views/timespan-settings.js | 2 +- client/views/view-initial-condition.js | 2 +- client/views/view-particle.js | 2 +- client/views/view-sweep-parameter.js | 2 +- client/views/workflow-group-listing.js | 2 +- client/views/workflow-info.js | 2 +- client/views/workflow-listing.js | 2 +- client/views/workflow-results.js | 2 +- client/views/workflow-status.js | 2 +- stochss/handlers/__init__.py | 2 +- stochss/handlers/file_browser.py | 2 +- stochss/handlers/log.py | 2 +- stochss/handlers/models.py | 2 +- stochss/handlers/pages.py | 2 +- stochss/handlers/project.py | 2 +- stochss/handlers/util/__init__.py | 2 +- stochss/handlers/util/ensemble_simulation.py | 2 +- stochss/handlers/util/parameter_scan.py | 2 +- stochss/handlers/util/parameter_sweep.py | 2 +- stochss/handlers/util/parameter_sweep_1d.py | 2 +- stochss/handlers/util/parameter_sweep_2d.py | 2 +- stochss/handlers/util/scripts/run_preview.py | 2 +- stochss/handlers/util/scripts/start_job.py | 2 +- stochss/handlers/util/scripts/upload_remote_file.py | 2 +- stochss/handlers/util/spatial_simulation.py | 2 +- stochss/handlers/util/stochss_base.py | 2 +- stochss/handlers/util/stochss_errors.py | 2 +- stochss/handlers/util/stochss_file.py | 2 +- stochss/handlers/util/stochss_folder.py | 2 +- stochss/handlers/util/stochss_job.py | 2 +- stochss/handlers/util/stochss_model.py | 2 +- stochss/handlers/util/stochss_notebook.py | 2 +- stochss/handlers/util/stochss_project.py | 2 +- stochss/handlers/util/stochss_sbml.py | 2 +- stochss/handlers/util/stochss_spatial_model.py | 2 +- stochss/handlers/util/stochss_workflow.py | 2 +- stochss/handlers/workflows.py | 2 +- stochss/tests/run_tests.py | 2 +- 155 files changed, 155 insertions(+), 155 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 99000953fe..7d089c27ab 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,5 +1,5 @@ StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/app.js b/client/app.js index 9aac1d1023..e809e61721 100644 --- a/client/app.js +++ b/client/app.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/config.js b/client/config.js index 8fb86111be..824477f41d 100644 --- a/client/config.js +++ b/client/config.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/graphics.js b/client/graphics.js index 3f0299ce16..33042874c4 100644 --- a/client/graphics.js +++ b/client/graphics.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/modals.js b/client/modals.js index 54b3aaadf2..93a5e044d9 100644 --- a/client/modals.js +++ b/client/modals.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/boundary-condition.js b/client/models/boundary-condition.js index 405d84e800..d8e893bfec 100644 --- a/client/models/boundary-condition.js +++ b/client/models/boundary-condition.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/boundary-conditions.js b/client/models/boundary-conditions.js index c156d8045f..667af7cd85 100644 --- a/client/models/boundary-conditions.js +++ b/client/models/boundary-conditions.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/creator.js b/client/models/creator.js index ee9d3be24d..d39dd3cf98 100644 --- a/client/models/creator.js +++ b/client/models/creator.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/creators.js b/client/models/creators.js index df67172530..7b23fb84e7 100644 --- a/client/models/creators.js +++ b/client/models/creators.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/domain-type.js b/client/models/domain-type.js index 2362909609..9c88aa284f 100644 --- a/client/models/domain-type.js +++ b/client/models/domain-type.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/domain-types.js b/client/models/domain-types.js index 0b4ad9e1bf..693625ae8a 100644 --- a/client/models/domain-types.js +++ b/client/models/domain-types.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/domain.js b/client/models/domain.js index 73c34cbe6a..725212644a 100644 --- a/client/models/domain.js +++ b/client/models/domain.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/event-assignment.js b/client/models/event-assignment.js index 7e621a7caf..b1452025b4 100644 --- a/client/models/event-assignment.js +++ b/client/models/event-assignment.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/event-assignments.js b/client/models/event-assignments.js index 62733ea5bf..21ed3349f1 100644 --- a/client/models/event-assignments.js +++ b/client/models/event-assignments.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/event.js b/client/models/event.js index a20050481e..6f30f26b6c 100644 --- a/client/models/event.js +++ b/client/models/event.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/events.js b/client/models/events.js index 25729358f1..904df78a1d 100644 --- a/client/models/events.js +++ b/client/models/events.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/function-definition.js b/client/models/function-definition.js index eef7d7caae..1ebd991677 100644 --- a/client/models/function-definition.js +++ b/client/models/function-definition.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/function-definitions.js b/client/models/function-definitions.js index 4dab2ad8bc..824c2634ad 100644 --- a/client/models/function-definitions.js +++ b/client/models/function-definitions.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/initial-condition.js b/client/models/initial-condition.js index 8f4801b10f..814c33048a 100644 --- a/client/models/initial-condition.js +++ b/client/models/initial-condition.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/initial-conditions.js b/client/models/initial-conditions.js index c6984f1a16..cd4fdd0604 100644 --- a/client/models/initial-conditions.js +++ b/client/models/initial-conditions.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/job.js b/client/models/job.js index d519da4f3c..33e0a37883 100644 --- a/client/models/job.js +++ b/client/models/job.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/jobs.js b/client/models/jobs.js index 01ecde1afb..9699ee7a10 100644 --- a/client/models/jobs.js +++ b/client/models/jobs.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/me.js b/client/models/me.js index 5fdbe15e6c..4bc370dc9d 100644 --- a/client/models/me.js +++ b/client/models/me.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/metadata.js b/client/models/metadata.js index 2a80da336c..873e4c9e96 100644 --- a/client/models/metadata.js +++ b/client/models/metadata.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/model.js b/client/models/model.js index 8802902eb4..b09b3a6540 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/parameter-sweep-settings.js b/client/models/parameter-sweep-settings.js index 1b746a5b2e..e1a09f5b93 100644 --- a/client/models/parameter-sweep-settings.js +++ b/client/models/parameter-sweep-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/parameter.js b/client/models/parameter.js index e8d0c0c5fe..1d460ecfa5 100644 --- a/client/models/parameter.js +++ b/client/models/parameter.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/parameters.js b/client/models/parameters.js index 5091fa2a45..76848f5a00 100644 --- a/client/models/parameters.js +++ b/client/models/parameters.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/particle.js b/client/models/particle.js index 5cb54d5e06..948633c14c 100644 --- a/client/models/particle.js +++ b/client/models/particle.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/particles.js b/client/models/particles.js index ec599b1024..388f50fe24 100644 --- a/client/models/particles.js +++ b/client/models/particles.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/project.js b/client/models/project.js index 404e84bc9f..6a168b6a12 100644 --- a/client/models/project.js +++ b/client/models/project.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/reaction.js b/client/models/reaction.js index 8802ef112d..94bfff06c4 100644 --- a/client/models/reaction.js +++ b/client/models/reaction.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/reactions.js b/client/models/reactions.js index e225df46fb..10b26f5d1b 100644 --- a/client/models/reactions.js +++ b/client/models/reactions.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/results-settings.js b/client/models/results-settings.js index ffae215597..e7d13203fe 100644 --- a/client/models/results-settings.js +++ b/client/models/results-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/rule.js b/client/models/rule.js index 9113bd7ea5..afe82692b8 100644 --- a/client/models/rule.js +++ b/client/models/rule.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/rules.js b/client/models/rules.js index cc6c9afac8..955c85a9fb 100644 --- a/client/models/rules.js +++ b/client/models/rules.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/settings.js b/client/models/settings.js index fe6911dcaa..9817082956 100644 --- a/client/models/settings.js +++ b/client/models/settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/simulation-settings.js b/client/models/simulation-settings.js index a67ede077b..af8c55a560 100644 --- a/client/models/simulation-settings.js +++ b/client/models/simulation-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/specie.js b/client/models/specie.js index d2c4f99d4b..8f1f8375b5 100644 --- a/client/models/specie.js +++ b/client/models/specie.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/species.js b/client/models/species.js index b9ee6dc508..8cde7ff383 100644 --- a/client/models/species.js +++ b/client/models/species.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/stoich-specie.js b/client/models/stoich-specie.js index a11311f9f5..6e109c8dc4 100644 --- a/client/models/stoich-specie.js +++ b/client/models/stoich-specie.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/stoich-species.js b/client/models/stoich-species.js index 53d398197f..0bfdbb44f9 100644 --- a/client/models/stoich-species.js +++ b/client/models/stoich-species.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/sweep-parameter.js b/client/models/sweep-parameter.js index e9efdf0a38..288c68c832 100644 --- a/client/models/sweep-parameter.js +++ b/client/models/sweep-parameter.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/sweep-parameters.js b/client/models/sweep-parameters.js index eb60b7f7c0..c421f54339 100644 --- a/client/models/sweep-parameters.js +++ b/client/models/sweep-parameters.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/timespan-settings.js b/client/models/timespan-settings.js index b467a2b125..0978e7eb46 100644 --- a/client/models/timespan-settings.js +++ b/client/models/timespan-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/workflow-group.js b/client/models/workflow-group.js index 2ba66cc014..364ac6f49b 100644 --- a/client/models/workflow-group.js +++ b/client/models/workflow-group.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/workflow-groups.js b/client/models/workflow-groups.js index a33b2dce18..3f416dedbd 100644 --- a/client/models/workflow-groups.js +++ b/client/models/workflow-groups.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/workflow.js b/client/models/workflow.js index 36427fdfd9..b015eb1389 100644 --- a/client/models/workflow.js +++ b/client/models/workflow.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/models/workflows.js b/client/models/workflows.js index 194fe9b0da..ac1af588a4 100644 --- a/client/models/workflows.js +++ b/client/models/workflows.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/page-help.js b/client/page-help.js index 855a2618fc..6207aaa081 100644 --- a/client/page-help.js +++ b/client/page-help.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/base.js b/client/pages/base.js index 4e295992ed..05cb8dec34 100644 --- a/client/pages/base.js +++ b/client/pages/base.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/domain-editor.js b/client/pages/domain-editor.js index 4160ab3d44..ef6bc1e3ab 100644 --- a/client/pages/domain-editor.js +++ b/client/pages/domain-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/file-browser.js b/client/pages/file-browser.js index 94dc7d5c75..080cc866dd 100644 --- a/client/pages/file-browser.js +++ b/client/pages/file-browser.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/home.js b/client/pages/home.js index 4b1b4121f4..039614d8b2 100644 --- a/client/pages/home.js +++ b/client/pages/home.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/loading-page.js b/client/pages/loading-page.js index f6ba556d4f..1710f889b0 100644 --- a/client/pages/loading-page.js +++ b/client/pages/loading-page.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/model-editor.js b/client/pages/model-editor.js index 01a6c1fccb..e15c3506d8 100644 --- a/client/pages/model-editor.js +++ b/client/pages/model-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/multiple-plots.js b/client/pages/multiple-plots.js index ad54c5b373..108f9b60ce 100644 --- a/client/pages/multiple-plots.js +++ b/client/pages/multiple-plots.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/page.js b/client/pages/page.js index 3e434457ef..d9c6743cc0 100644 --- a/client/pages/page.js +++ b/client/pages/page.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/project-browser.js b/client/pages/project-browser.js index 1b65377b8d..6286c611a8 100644 --- a/client/pages/project-browser.js +++ b/client/pages/project-browser.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/project-manager.js b/client/pages/project-manager.js index 67f45073ba..0ac554d7ce 100644 --- a/client/pages/project-manager.js +++ b/client/pages/project-manager.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/quickstart.js b/client/pages/quickstart.js index bf64e271f7..5fdd276d9c 100644 --- a/client/pages/quickstart.js +++ b/client/pages/quickstart.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/users-home.js b/client/pages/users-home.js index c5fa5b81c5..2eb2e8ee93 100644 --- a/client/pages/users-home.js +++ b/client/pages/users-home.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/workflow-manager.js b/client/pages/workflow-manager.js index 3dd79cdc2e..0e1696a6b0 100644 --- a/client/pages/workflow-manager.js +++ b/client/pages/workflow-manager.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/pages/workflow-selection.js b/client/pages/workflow-selection.js index 71846a1d2b..3d28c8c114 100644 --- a/client/pages/workflow-selection.js +++ b/client/pages/workflow-selection.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/reaction-types.js b/client/reaction-types.js index 503db760d3..ada8f44efb 100644 --- a/client/reaction-types.js +++ b/client/reaction-types.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/styles/styles.css b/client/styles/styles.css index e5b21c765d..f437041f88 100644 --- a/client/styles/styles.css +++ b/client/styles/styles.css @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/tooltips.js b/client/tooltips.js index 18fce2f456..8149af6490 100644 --- a/client/tooltips.js +++ b/client/tooltips.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/archive-listing.js b/client/views/archive-listing.js index e5bb93c18b..9aacef63b1 100644 --- a/client/views/archive-listing.js +++ b/client/views/archive-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/boundary-condition-view.js b/client/views/boundary-condition-view.js index a5d5a0e05a..a91cf1b9fb 100644 --- a/client/views/boundary-condition-view.js +++ b/client/views/boundary-condition-view.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index 1255953dc2..60a27794cd 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/component-types.js b/client/views/component-types.js index 212ef27413..f6001e6b88 100644 --- a/client/views/component-types.js +++ b/client/views/component-types.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/creator-listing.js b/client/views/creator-listing.js index 847f8f1b92..016b6e5d21 100644 --- a/client/views/creator-listing.js +++ b/client/views/creator-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/domain-viewer.js b/client/views/domain-viewer.js index 0ac2e490bb..2ff51bcc60 100644 --- a/client/views/domain-viewer.js +++ b/client/views/domain-viewer.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-3D-domain.js b/client/views/edit-3D-domain.js index 5513d14991..e1161faf3f 100644 --- a/client/views/edit-3D-domain.js +++ b/client/views/edit-3D-domain.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-custom-stoich-specie.js b/client/views/edit-custom-stoich-specie.js index 5db31730e7..0e842387eb 100644 --- a/client/views/edit-custom-stoich-specie.js +++ b/client/views/edit-custom-stoich-specie.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-domain-type.js b/client/views/edit-domain-type.js index 455cf02da3..5460f753ba 100644 --- a/client/views/edit-domain-type.js +++ b/client/views/edit-domain-type.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-event-assignment.js b/client/views/edit-event-assignment.js index 8e48f4931f..8bb53ad728 100644 --- a/client/views/edit-event-assignment.js +++ b/client/views/edit-event-assignment.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-function-definition.js b/client/views/edit-function-definition.js index bb7146befb..d582b9dba6 100644 --- a/client/views/edit-function-definition.js +++ b/client/views/edit-function-definition.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-initial-condition.js b/client/views/edit-initial-condition.js index 11089e5efc..757d2c7ca5 100644 --- a/client/views/edit-initial-condition.js +++ b/client/views/edit-initial-condition.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-parameter.js b/client/views/edit-parameter.js index e8b342f24c..bc2bec13e9 100644 --- a/client/views/edit-parameter.js +++ b/client/views/edit-parameter.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-particle.js b/client/views/edit-particle.js index 099e0a099a..8dc1f0d2da 100644 --- a/client/views/edit-particle.js +++ b/client/views/edit-particle.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-project.js b/client/views/edit-project.js index 042dddc754..2dcf7d3f90 100644 --- a/client/views/edit-project.js +++ b/client/views/edit-project.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-rule.js b/client/views/edit-rule.js index de9884510f..66e36128b8 100644 --- a/client/views/edit-rule.js +++ b/client/views/edit-rule.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-species.js b/client/views/edit-species.js index 2aad005f6c..39a7e6f744 100644 --- a/client/views/edit-species.js +++ b/client/views/edit-species.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/edit-stoich-specie.js b/client/views/edit-stoich-specie.js index 162806b2e6..4f276620aa 100644 --- a/client/views/edit-stoich-specie.js +++ b/client/views/edit-stoich-specie.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/event-assignments-editor.js b/client/views/event-assignments-editor.js index 7f790869f5..3640c137e9 100644 --- a/client/views/event-assignments-editor.js +++ b/client/views/event-assignments-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/event-details.js b/client/views/event-details.js index b0cc2aca09..a4cf72f6c9 100644 --- a/client/views/event-details.js +++ b/client/views/event-details.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/event-listings.js b/client/views/event-listings.js index 658828a165..0a7c7866fe 100644 --- a/client/views/event-listings.js +++ b/client/views/event-listings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/events-editor.js b/client/views/events-editor.js index 189c16fad5..ff6b3b34f8 100644 --- a/client/views/events-editor.js +++ b/client/views/events-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/file-browser-view.js b/client/views/file-browser-view.js index de8ef0e6d2..b60f2bf2b9 100644 --- a/client/views/file-browser-view.js +++ b/client/views/file-browser-view.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/initial-conditions-editor.js b/client/views/initial-conditions-editor.js index 5e017ecde2..fbebfda626 100644 --- a/client/views/initial-conditions-editor.js +++ b/client/views/initial-conditions-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/initial-conditions-viewer.js b/client/views/initial-conditions-viewer.js index 777c445851..66bfc3e51f 100644 --- a/client/views/initial-conditions-viewer.js +++ b/client/views/initial-conditions-viewer.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/input.js b/client/views/input.js index 4f4ee98a1a..7516be3463 100644 --- a/client/views/input.js +++ b/client/views/input.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/job-listing.js b/client/views/job-listing.js index 2398dc5861..e24c2166c9 100644 --- a/client/views/job-listing.js +++ b/client/views/job-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/main.js b/client/views/main.js index d432a10296..a62a9a4c2c 100644 --- a/client/views/main.js +++ b/client/views/main.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/mesh-editor.js b/client/views/mesh-editor.js index c3054cd9db..b9e9945025 100644 --- a/client/views/mesh-editor.js +++ b/client/views/mesh-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/meta-data.js b/client/views/meta-data.js index 37180ea829..f2f459e2a6 100644 --- a/client/views/meta-data.js +++ b/client/views/meta-data.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/model-listing.js b/client/views/model-listing.js index 5d753e17fe..24a37cc6a4 100644 --- a/client/views/model-listing.js +++ b/client/views/model-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/model-state-buttons.js b/client/views/model-state-buttons.js index 4683565571..82f4e40412 100644 --- a/client/views/model-state-buttons.js +++ b/client/views/model-state-buttons.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index b1563030d3..c1919f2440 100644 --- a/client/views/model-viewer.js +++ b/client/views/model-viewer.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/parameter-settings.js b/client/views/parameter-settings.js index 50f47229df..dcaba4fce5 100644 --- a/client/views/parameter-settings.js +++ b/client/views/parameter-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/parameters-editor.js b/client/views/parameters-editor.js index aa72c100c5..d7623c1a93 100644 --- a/client/views/parameters-editor.js +++ b/client/views/parameters-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/quickview-domain-types.js b/client/views/quickview-domain-types.js index 5b003200d3..967715448c 100644 --- a/client/views/quickview-domain-types.js +++ b/client/views/quickview-domain-types.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/reactant-product.js b/client/views/reactant-product.js index 7bd3858693..8a982bcc03 100644 --- a/client/views/reactant-product.js +++ b/client/views/reactant-product.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/reaction-details.js b/client/views/reaction-details.js index aa074e024b..00f27574a7 100644 --- a/client/views/reaction-details.js +++ b/client/views/reaction-details.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/reaction-listing.js b/client/views/reaction-listing.js index 6de8a56d3e..2503be8ee3 100644 --- a/client/views/reaction-listing.js +++ b/client/views/reaction-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/reaction-types.js b/client/views/reaction-types.js index f931b17510..e57eddcc06 100644 --- a/client/views/reaction-types.js +++ b/client/views/reaction-types.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/reactions-editor.js b/client/views/reactions-editor.js index dc9b56ed77..e6539a856e 100644 --- a/client/views/reactions-editor.js +++ b/client/views/reactions-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/rules-editor.js b/client/views/rules-editor.js index 6c0145acad..489a41e770 100644 --- a/client/views/rules-editor.js +++ b/client/views/rules-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/sbml-component-editor.js b/client/views/sbml-component-editor.js index e7f58940ab..678984682d 100644 --- a/client/views/sbml-component-editor.js +++ b/client/views/sbml-component-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/settings-viewer.js b/client/views/settings-viewer.js index 918bfddf2b..bb45c043a6 100644 --- a/client/views/settings-viewer.js +++ b/client/views/settings-viewer.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/settings.js b/client/views/settings.js index 3cec47d4f8..4905e37461 100644 --- a/client/views/settings.js +++ b/client/views/settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/simulation-settings.js b/client/views/simulation-settings.js index 024c7d381b..f6300beac2 100644 --- a/client/views/simulation-settings.js +++ b/client/views/simulation-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/species-editor.js b/client/views/species-editor.js index 6eb3a6b445..9ac3ae456e 100644 --- a/client/views/species-editor.js +++ b/client/views/species-editor.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/sweep-parameter-range.js b/client/views/sweep-parameter-range.js index 9b297388dc..00faa0a625 100644 --- a/client/views/sweep-parameter-range.js +++ b/client/views/sweep-parameter-range.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/sweep-parameter.js b/client/views/sweep-parameter.js index 29a092bdf3..1aebd9a482 100644 --- a/client/views/sweep-parameter.js +++ b/client/views/sweep-parameter.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/tests.js b/client/views/tests.js index 057fcf283a..ebee33007a 100644 --- a/client/views/tests.js +++ b/client/views/tests.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/timespan-settings.js b/client/views/timespan-settings.js index fa5f87cb4b..62b59c1a21 100644 --- a/client/views/timespan-settings.js +++ b/client/views/timespan-settings.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/view-initial-condition.js b/client/views/view-initial-condition.js index 451e5356e0..49ae01f0ae 100644 --- a/client/views/view-initial-condition.js +++ b/client/views/view-initial-condition.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/view-particle.js b/client/views/view-particle.js index 7126ff21fe..4948a2804d 100644 --- a/client/views/view-particle.js +++ b/client/views/view-particle.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/view-sweep-parameter.js b/client/views/view-sweep-parameter.js index bc64a1dab2..10d6b01fa3 100644 --- a/client/views/view-sweep-parameter.js +++ b/client/views/view-sweep-parameter.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/workflow-group-listing.js b/client/views/workflow-group-listing.js index 1e1d322211..c51e723eea 100644 --- a/client/views/workflow-group-listing.js +++ b/client/views/workflow-group-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/workflow-info.js b/client/views/workflow-info.js index cefad55a32..526ddc3514 100644 --- a/client/views/workflow-info.js +++ b/client/views/workflow-info.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/workflow-listing.js b/client/views/workflow-listing.js index 7ee0006b89..cde0be60b9 100644 --- a/client/views/workflow-listing.js +++ b/client/views/workflow-listing.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/workflow-results.js b/client/views/workflow-results.js index adc075079f..b0a9d8a153 100644 --- a/client/views/workflow-results.js +++ b/client/views/workflow-results.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/client/views/workflow-status.js b/client/views/workflow-status.js index c4d0947c39..ee221168f5 100644 --- a/client/views/workflow-status.js +++ b/client/views/workflow-status.js @@ -1,6 +1,6 @@ /* StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/__init__.py b/stochss/handlers/__init__.py index f1ae51967e..ce3865a3a9 100644 --- a/stochss/handlers/__init__.py +++ b/stochss/handlers/__init__.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/file_browser.py b/stochss/handlers/file_browser.py index 50fbbb523f..2afb26ca41 100644 --- a/stochss/handlers/file_browser.py +++ b/stochss/handlers/file_browser.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/log.py b/stochss/handlers/log.py index 550e2473be..9d6d78bcd8 100644 --- a/stochss/handlers/log.py +++ b/stochss/handlers/log.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/models.py b/stochss/handlers/models.py index 67a5042ff5..545c69c0b4 100644 --- a/stochss/handlers/models.py +++ b/stochss/handlers/models.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/pages.py b/stochss/handlers/pages.py index 23cece06b1..e2961bb4fa 100644 --- a/stochss/handlers/pages.py +++ b/stochss/handlers/pages.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/project.py b/stochss/handlers/project.py index b0b727364a..fe1e95f962 100644 --- a/stochss/handlers/project.py +++ b/stochss/handlers/project.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/__init__.py b/stochss/handlers/util/__init__.py index eda8912745..d7351abbfc 100644 --- a/stochss/handlers/util/__init__.py +++ b/stochss/handlers/util/__init__.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/ensemble_simulation.py b/stochss/handlers/util/ensemble_simulation.py index 2b220e21fb..d2697e06db 100644 --- a/stochss/handlers/util/ensemble_simulation.py +++ b/stochss/handlers/util/ensemble_simulation.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/parameter_scan.py b/stochss/handlers/util/parameter_scan.py index be84dd7ef4..67e72e85a6 100644 --- a/stochss/handlers/util/parameter_scan.py +++ b/stochss/handlers/util/parameter_scan.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/parameter_sweep.py b/stochss/handlers/util/parameter_sweep.py index d1a4a909c2..43251bfabd 100644 --- a/stochss/handlers/util/parameter_sweep.py +++ b/stochss/handlers/util/parameter_sweep.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/parameter_sweep_1d.py b/stochss/handlers/util/parameter_sweep_1d.py index 33c580e115..e1744532a8 100644 --- a/stochss/handlers/util/parameter_sweep_1d.py +++ b/stochss/handlers/util/parameter_sweep_1d.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/parameter_sweep_2d.py b/stochss/handlers/util/parameter_sweep_2d.py index 0c909181de..b9718cc710 100644 --- a/stochss/handlers/util/parameter_sweep_2d.py +++ b/stochss/handlers/util/parameter_sweep_2d.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/scripts/run_preview.py b/stochss/handlers/util/scripts/run_preview.py index 89cd9c6c1f..97c07c6253 100755 --- a/stochss/handlers/util/scripts/run_preview.py +++ b/stochss/handlers/util/scripts/run_preview.py @@ -2,7 +2,7 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/scripts/start_job.py b/stochss/handlers/util/scripts/start_job.py index fa880431a8..50996c50a7 100755 --- a/stochss/handlers/util/scripts/start_job.py +++ b/stochss/handlers/util/scripts/start_job.py @@ -2,7 +2,7 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/scripts/upload_remote_file.py b/stochss/handlers/util/scripts/upload_remote_file.py index 5db60cc1b8..e92073cdd7 100755 --- a/stochss/handlers/util/scripts/upload_remote_file.py +++ b/stochss/handlers/util/scripts/upload_remote_file.py @@ -2,7 +2,7 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/spatial_simulation.py b/stochss/handlers/util/spatial_simulation.py index 955af08715..3741e1dbf5 100644 --- a/stochss/handlers/util/spatial_simulation.py +++ b/stochss/handlers/util/spatial_simulation.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_base.py b/stochss/handlers/util/stochss_base.py index c17075e87f..b08ba39b44 100644 --- a/stochss/handlers/util/stochss_base.py +++ b/stochss/handlers/util/stochss_base.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_errors.py b/stochss/handlers/util/stochss_errors.py index 3ee1cbd411..076916205c 100644 --- a/stochss/handlers/util/stochss_errors.py +++ b/stochss/handlers/util/stochss_errors.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_file.py b/stochss/handlers/util/stochss_file.py index fbe99c91d3..2c621494fc 100644 --- a/stochss/handlers/util/stochss_file.py +++ b/stochss/handlers/util/stochss_file.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_folder.py b/stochss/handlers/util/stochss_folder.py index d9dbc44d4c..70a7a20bb9 100644 --- a/stochss/handlers/util/stochss_folder.py +++ b/stochss/handlers/util/stochss_folder.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_job.py b/stochss/handlers/util/stochss_job.py index 612c513b62..b9beeaa818 100644 --- a/stochss/handlers/util/stochss_job.py +++ b/stochss/handlers/util/stochss_job.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_model.py b/stochss/handlers/util/stochss_model.py index 6b617b2385..1eca031ec1 100644 --- a/stochss/handlers/util/stochss_model.py +++ b/stochss/handlers/util/stochss_model.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_notebook.py b/stochss/handlers/util/stochss_notebook.py index 62654a378c..ee84eaf346 100644 --- a/stochss/handlers/util/stochss_notebook.py +++ b/stochss/handlers/util/stochss_notebook.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_project.py b/stochss/handlers/util/stochss_project.py index 3b2c9327a4..215039b760 100644 --- a/stochss/handlers/util/stochss_project.py +++ b/stochss/handlers/util/stochss_project.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_sbml.py b/stochss/handlers/util/stochss_sbml.py index 7f3cf29486..4d4f334880 100644 --- a/stochss/handlers/util/stochss_sbml.py +++ b/stochss/handlers/util/stochss_sbml.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index 99f607d217..1455c38792 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/util/stochss_workflow.py b/stochss/handlers/util/stochss_workflow.py index c4646e5304..176c2a29e8 100644 --- a/stochss/handlers/util/stochss_workflow.py +++ b/stochss/handlers/util/stochss_workflow.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/handlers/workflows.py b/stochss/handlers/workflows.py index cb37ad715a..ae8bbc4c63 100644 --- a/stochss/handlers/workflows.py +++ b/stochss/handlers/workflows.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/tests/run_tests.py b/stochss/tests/run_tests.py index 5aa6cce0f0..56a10113f8 100755 --- a/stochss/tests/run_tests.py +++ b/stochss/tests/run_tests.py @@ -2,7 +2,7 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 From 9f3f4b536ccdfe678abeba1a459aee03f5a8b634 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 14:02:46 -0400 Subject: [PATCH 123/133] Updated the copyright to 2021. --- stochss/tests/test_model_template.py | 2 +- stochss/tests/test_settings_template.py | 2 +- stochss/tests/test_stochss_base.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stochss/tests/test_model_template.py b/stochss/tests/test_model_template.py index c2bb805551..3e6812f079 100644 --- a/stochss/tests/test_model_template.py +++ b/stochss/tests/test_model_template.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/tests/test_settings_template.py b/stochss/tests/test_settings_template.py index 40b7ee3a98..8db314b230 100644 --- a/stochss/tests/test_settings_template.py +++ b/stochss/tests/test_settings_template.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 diff --git a/stochss/tests/test_stochss_base.py b/stochss/tests/test_stochss_base.py index d4f0bbee7e..81c5c979d3 100644 --- a/stochss/tests/test_stochss_base.py +++ b/stochss/tests/test_stochss_base.py @@ -1,6 +1,6 @@ ''' StochSS is a platform for simulating biochemical systems -Copyright (C) 2019-2020 StochSS developers. +Copyright (C) 2019-2021 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 From 24a3ba24ab315f4067a13706e2022d9998339833 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 14:53:14 -0400 Subject: [PATCH 124/133] Added css to darken the background color of the card header class. --- client/styles/styles.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/styles/styles.css b/client/styles/styles.css index f437041f88..64935dff14 100644 --- a/client/styles/styles.css +++ b/client/styles/styles.css @@ -702,3 +702,7 @@ span.checkbox { overflow-y: hidden; white-space: nowrap; } + +.card-header { + background-color: rgba(0, 0, 0, 0.1); +} From ed61c9323d2eb48b7f4bee76d6565f04cbef310f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 15:08:28 -0400 Subject: [PATCH 125/133] Updated the type for initial condition properties to fix type set conflicts. --- client/models/initial-condition.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/models/initial-condition.js b/client/models/initial-condition.js index b6fcf8d7d2..97c03bbec8 100644 --- a/client/models/initial-condition.js +++ b/client/models/initial-condition.js @@ -24,11 +24,11 @@ module.exports = State.extend({ props: { icType: 'string', annotation: 'string', - count: 'number', + count: 'any', types: 'object', - x: 'number', - y: 'number', - z: 'number', + x: 'any', + y: 'any', + z: 'any', }, children: { specie: Specie, From c22c7331437be63a36d988911d8b3a62fee4e85e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 16:54:06 -0400 Subject: [PATCH 126/133] Refactored the boundary condition front end to send the model path to the api handler. Refactored the boundary condition api hander and create boundary condition function to pass the model to the spatialpy boundary condition constructor. Refactored the value inputs to support vector inputs for velocity. --- .../includes/boundaryConditionsEditor.pug | 8 +- client/views/boundary-conditions-editor.js | 83 +++++++++++++++++-- stochss/handlers/models.py | 7 +- .../handlers/util/stochss_spatial_model.py | 6 +- 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug index 61dc60bbc7..ef9ea6aa1e 100644 --- a/client/templates/includes/boundaryConditionsEditor.pug +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -76,7 +76,13 @@ div#boundary-conditions.card td(data-hook="new-bc-target") tr th(scope="row") Value - td(data-hook="new-bc-value" data-target="value") + td + div(data-hook="new-bc-velocity-value") + div(data-hook="new-bc-value-x" data-target="value") + div(data-hook="new-bc-value-y" data-target="value") + div(data-hook="new-bc-value-z" data-target="value") + div(data-hook="new-bc-other-value" style="display: none") + div(data-hook="new-bc-value" data-target="value") tr th(scope="row") Concentration td diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index 60a27794cd..d362d96798 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -38,6 +38,9 @@ module.exports = View.extend({ 'change [data-hook=new-bc-deterministic]' : 'handleSetDeterministic', 'change [data-hook=new-bc-type]' : 'handleSetValue', 'change [data-hook=new-bc-value]' : 'handleSetValue', + 'change [data-hook=new-bc-value-x]' : 'handleSetValue', + 'change [data-hook=new-bc-value-y]' : 'handleSetValue', + 'change [data-hook=new-bc-value-z]' : 'handleSetValue', 'change [data-hook=new-bc-x-min]' : 'handleSetValue', 'change [data-hook=new-bc-x-max]' : 'handleSetValue', 'change [data-hook=new-bc-y-min]' : 'handleSetValue', @@ -77,7 +80,13 @@ module.exports = View.extend({ handleAddBCClick: function (e) { let endpoint = path.join(app.getApiPath(), "model/new-bc") let self = this; - app.postXHR(endpoint, this.newBC, { + if(this.newBC.property === "v") { + this.newBC.value = this.newBCVector + } + let data = {model_path: this.collection.parent.directory, + kwargs: this.newBC} + console.log(data) + app.postXHR(endpoint, data, { success: function (err, response, body) { self.collection.addNewBoundaryCondition(self.newBCName, body.expression); self.setDefaultBC(); @@ -92,12 +101,22 @@ module.exports = View.extend({ let properties = ["v", "nu", "rho"]; let target = e.target.value; if(properties.includes(target)) { + if(target === "v") { + $(this.queryByHook("new-bc-other-value")).css("display", "none"); + $(this.queryByHook("new-bc-velocity-value")).css("display", "block"); + }else { + $(this.queryByHook("new-bc-velocity-value")).css("display", "none"); + $(this.queryByHook("new-bc-other-value")).css("display", "block"); + } this.newBC.property = target; this.newBC.species = null; $(this.queryByHook("new-bc-deterministic")).prop("disabled", true); }else{ + let species = this.collection.parent.species.filter(function (specie) { + return specie.compID === Number(target) + })[0].name; this.newBC.property = null; - this.newBC.species = target; + this.newBC.species = species; $(this.queryByHook("new-bc-deterministic")).prop("disabled", false); } }, @@ -108,11 +127,18 @@ module.exports = View.extend({ this.newBCName = value; }else{ if(key.endsWith("min") || key.endsWith("max") || key === "type_id"){ - value = this.validateNewBCCondition(key, value); - }else if(key === "value" && value === "") { - value = null; + this.newBC[key] = this.validateNewBCCondition(key, value); + }else if(key === "value") { + if(e.delegateTarget.dataset.hook.endsWith("x")) { + this.newBCVector[0] = value === "" ? 0 : value + }else if(e.delegateTarget.dataset.hook.endsWith("y")) { + this.newBCVector[1] = value === "" ? 0 : value + }else if(e.delegateTarget.dataset.hook.endsWith("z")) { + this.newBCVector[2] = value === "" ? 0 : value + }else{ + this.newBC[key] = value === "" ? null : value + } } - this.newBC[key] = value; } this.toggleAddNewBCButton(); }, @@ -169,10 +195,13 @@ module.exports = View.extend({ this.newBCName = ""; this.newBC = {"species": null, "property": "v", "value": null, "deterministic": true, "type_id": null, "xmin": null, "ymin": null, "zmin": null, "xmax": null, "ymax": null, "zmax": null}; + this.newBCVector = [0, 0, 0] this.setConditions = []; }, toggleAddNewBCButton: function () { - let disabled = this.newBCName === "" || this.newBC.value === null || !this.setConditions.length; + let invalidName = this.newBCName === "" + let invalidValue = this.newBC.property === "v" ? this.newBCVector === [0, 0, 0] : this.newBC.value === null + let disabled = invalidName || invalidValue || !this.setConditions.length; $(this.queryByHook("add-new-bc")).prop("disabled", disabled); }, update: function (e) {}, @@ -229,7 +258,45 @@ module.exports = View.extend({ name: 'value', tests: tests.valueTests, valueType: 'number', - value: this.newBC.value + }); + } + }, + newBCValueX: { + hook: "new-bc-value-x", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'value-x', + tests: tests.valueTests, + valueType: 'number', + label: "X: " + }); + } + }, + newBCValueY: { + hook: "new-bc-value-y", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'value-y', + tests: tests.valueTests, + valueType: 'number', + label: "Y: " + }); + } + }, + newBCValueZ: { + hook: "new-bc-value-z", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'value-z', + tests: tests.valueTests, + valueType: 'number', + label: "Z: " }); } }, diff --git a/stochss/handlers/models.py b/stochss/handlers/models.py index 545c69c0b4..04c210ad94 100644 --- a/stochss/handlers/models.py +++ b/stochss/handlers/models.py @@ -423,11 +423,14 @@ async def post(self): ---------- ''' self.set_header('Content-Type', 'application/json') - kwargs = json.loads(self.request.body.decode()) + data = json.loads(self.request.body.decode()) + path = data['model_path'] + kwargs = data['kwargs'] log.debug("Args passed to the boundary condition constructor: %s", kwargs) try: log.info("Creating the new boundary condition") - resp = StochSSSpatialModel.create_boundary_condition(kwargs) + model = StochSSSpatialModel(path=path) + resp = model.create_boundary_condition(kwargs) log.info("Successfully created the new boundary condition") log.debug("Response Message: %s", resp) self.write(resp) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index 1455c38792..ab481090d2 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -389,8 +389,7 @@ def convert_to_spatialpy(self): return s_model - @classmethod - def create_boundary_condition(cls, kwargs): + def create_boundary_condition(self, kwargs): ''' Create a new boundary condition using spatialpy.BoundaryCondition @@ -399,7 +398,8 @@ def create_boundary_condition(cls, kwargs): kwargs : dict Arguments passed to the spatialpy.BoundaryCondition constructor ''' - new_bc = BoundaryCondition(**kwargs) + model = self.convert_to_spatialpy() + new_bc = BoundaryCondition(model=model, **kwargs) expression = new_bc.expression() return {"expression": expression} From 2105d0d4848e3b017dcbacc131aa0ffae796161d Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Tue, 29 Jun 2021 17:12:27 -0400 Subject: [PATCH 127/133] Fixed autosave function. --- client/models/model.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/models/model.js b/client/models/model.js index b09b3a6540..9799e6e4f2 100644 --- a/client/models/model.js +++ b/client/models/model.js @@ -16,6 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +let _ = require('underscore'); +//support files var app = require('../app'); var path = require('path'); //models @@ -128,7 +130,7 @@ module.exports = Model.extend({ autoSave: function () { let self = this; setTimeout(function () { - app.postXHR(self.url(), self.toJSON(), { success: self.autoSave }); + app.postXHR(self.url(), self.toJSON(), { success: _.bind(self.autoSave, self) }); }, 120000); }, //called when save button is clicked From 486bc024b94c3dd73973c1d25fad499d8944a0a6 Mon Sep 17 00:00:00 2001 From: BryanRumsey <44621966+BryanRumsey@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:38:51 -0400 Subject: [PATCH 128/133] Fixed error display css --- client/templates/includes/boundaryConditionsEditor.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug index ef9ea6aa1e..fea9fc7d4b 100644 --- a/client/templates/includes/boundaryConditionsEditor.pug +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -90,7 +90,7 @@ div#boundary-conditions.card div.col-sm-6 - div + div(style="display: flex;") span.inline.mr-3(for="new-bc-type") Type: div.inline(id="new-bc-type" data-hook="new-bc-type" data-target="type_id") From 8693d747c239fa9ad9cc77edeee2fe233278f8a4 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 30 Jun 2021 13:45:49 -0400 Subject: [PATCH 129/133] Resolved review comments. --- client/views/boundary-conditions-editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js index d362d96798..bfc9607a00 100644 --- a/client/views/boundary-conditions-editor.js +++ b/client/views/boundary-conditions-editor.js @@ -85,7 +85,6 @@ module.exports = View.extend({ } let data = {model_path: this.collection.parent.directory, kwargs: this.newBC} - console.log(data) app.postXHR(endpoint, data, { success: function (err, response, body) { self.collection.addNewBoundaryCondition(self.newBCName, body.expression); From 938041c7a2500befad7b1972e25fa76d2dcb93a6 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 1 Jul 2021 11:30:39 -0400 Subject: [PATCH 130/133] Added gillespy2 dependency tests to the unit test suite. --- stochss/tests/run_tests.py | 2 + stochss/tests/test_gillespy2.py | 162 ++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 stochss/tests/test_gillespy2.py diff --git a/stochss/tests/run_tests.py b/stochss/tests/run_tests.py index b7d5d6d5f7..896b1a3be4 100755 --- a/stochss/tests/run_tests.py +++ b/stochss/tests/run_tests.py @@ -37,10 +37,12 @@ import test_model_template import test_settings_template import test_stochss_base + import test_gillespy2 modules = [ test_model_template, test_settings_template, + test_gillespy2, test_stochss_base ] diff --git a/stochss/tests/test_gillespy2.py b/stochss/tests/test_gillespy2.py new file mode 100644 index 0000000000..636f356ff7 --- /dev/null +++ b/stochss/tests/test_gillespy2.py @@ -0,0 +1,162 @@ +''' +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2021 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 unittest + +from gillespy2 import GillesPySolver + +from stochss.handlers import StochSSModel + +os.chdir('/stochss') + +class TestModelTemplate(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for GillesPy2 dependency. + ################################################################################################ + ''' + def setUp(self): + ''' Create a list of common example paths for each test. ''' + self.model_paths = ["Brusselator/Brusselator.mdl", + "Degradation/Degradation.mdl", + "Dimerization/Dimerization.mdl", + "Lotkavolterra_Oscillator/Lotkavolterra_Oscillator.mdl", + "Michaelis_Menten/Michaelis_Menten.mdl", + "Opioid/Opioid.mdl", + "Schlogl/Schlogl.mdl", + "Toggle_Switch/Toggle_Switch.mdl", + "Vilar_Oscillator/Vilar_Oscillator.mdl" + ] + + ################################################################################################ + # Unit tests for GillesPy2 dependency check_cpp_support. + ################################################################################################ + + def test_check_cpp_support(self): + ''' Check if the check cpp support functions works in StochSS. ''' + from gillespy2.solvers.utilities.cpp_support_test import check_cpp_support + + self.assertIsInstance(check_cpp_support(), bool) + + ################################################################################################ + # Unit tests for GillesPy2 dependency get_best_solver. + ################################################################################################ + + def test_get_best_solver(self): + ''' Check if the get best solver function works in StochSS. ''' + test_model = StochSSModel(path=os.path.join("Examples", self.model_paths[0])).convert_to_gillespy2() + test_solver = test_model.get_best_solver() + self.assertIsInstance(test_solver(), GillesPySolver) + + ################################################################################################ + # Unit tests for GillesPy2 dependency get_best_solver_algo. + ################################################################################################ + + def test_get_best_solver_algo(self): + ''' Check if the get best solver algo function works in StochSS. ''' + test_algos = ["ODE", "SSA", "Tau-Leaping"] + test_model = StochSSModel(path=os.path.join("Examples", self.model_paths[0])).convert_to_gillespy2() + for test_algo in test_algos: + with self.subTest(test_algo=test_algo): + test_solver = test_model.get_best_solver_algo(algorithm=test_algo) + self.assertIsInstance(test_solver(), GillesPySolver) + + ################################################################################################ + # Unit tests for GillesPy2 dependency solvers. + ################################################################################################ + + def test_ode_solver(self): + ''' Check if the test_models run with the ODESolver. ''' + from gillespy2 import ODESolver + + self.model_paths.append("Oregonator/Oregonator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_model.run(solver=ODESolver) + + + def test_ode_c_solver(self): + ''' Check if the test_models run with the ODECSolver. ''' + from gillespy2 import ODECSolver + + self.model_paths.append("Oregonator/Oregonator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_solver = ODECSolver(model=test_model) + test_model.run(solver=test_solver) + + + def test_numpy_ssa_solver(self): + ''' Check if the test_models run with the NumPySSASolver. ''' + from gillespy2 import NumPySSASolver + + self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_model.run(solver=NumPySSASolver) + + + def test_ssa_c_solver(self): + ''' Check if the test_models run with the SSACSolver. ''' + from gillespy2 import SSACSolver + + self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_solver = SSACSolver(model=test_model) + test_model.run(solver=test_solver) + + + def test_tau_leaping_solver(self): + ''' Check if the test_models run with the TauLeapingSolver. ''' + from gillespy2 import TauLeapingSolver + + self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_model.run(solver=TauLeapingSolver) + + + def test_tau_leaping_c_solver(self): + ''' Check if the test_models run with the TauLeapingCSolver. ''' + from gillespy2 import TauLeapingCSolver + + self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_solver = TauLeapingCSolver(model=test_model) + test_model.run(solver=test_solver) + + + def test_tau_hybrid_solver(self): + ''' Check if the test_models run with the TauHybridSolver. ''' + from gillespy2 import TauHybridSolver + + self.model_paths.append("Oregonator/Oregonator.mdl") + self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") + for model_path in self.model_paths: + with self.subTest(model=model_path.split('/').pop()): + test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() + test_model.run(solver=TauHybridSolver) From f1a09b1e0c6fa506f6b5a738731b5aecbdc8cbca Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 1 Jul 2021 12:44:51 -0400 Subject: [PATCH 131/133] Moved the models being tested for the gillespy2 dependency test to an example models file. --- stochss/tests/example_models.py | 352 ++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 stochss/tests/example_models.py diff --git a/stochss/tests/example_models.py b/stochss/tests/example_models.py new file mode 100644 index 0000000000..17c415ac7c --- /dev/null +++ b/stochss/tests/example_models.py @@ -0,0 +1,352 @@ +''' +StochSS is a platform for simulating biochemical systems +Copyright (C) 2019-2021 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 numpy as np + +from gillespy2.core import ( + Model, + Species, + Reaction, + Parameter +) + +# pylint: disable=line-too-long +# pylint: disable=missing-class-docstring +# pylint: disable=too-few-public-methods +class Brusselator(Model): + def __init__(self): + Model.__init__(self, name="Brusselator") + self.volume = 1000 + + # Parameters + self.add_parameter(Parameter(name="rate1", expression="5000")) + self.add_parameter(Parameter(name="rate2", expression="50")) + self.add_parameter(Parameter(name="rate3", expression="5e-05")) + self.add_parameter(Parameter(name="rate4", expression="5")) + + # Variables + self.add_species(Species(name="A", initial_value=100000, mode="discrete")) + self.add_species(Species(name="B", initial_value=100000, mode="discrete")) + self.add_species(Species(name="C", initial_value=0, mode="discrete")) + self.add_species(Species(name="D", initial_value=0, mode="discrete")) + self.add_species(Species(name="X", initial_value=2000, mode="discrete")) + self.add_species(Species(name="Y", initial_value=1000, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="reaction1", reactants={'A': 1}, products={'X': 1, 'A': 1}, propensity_function="rate1")) + self.add_reaction(Reaction(name="reaction2", reactants={'B': 1, 'X': 1}, products={'Y': 1, 'C': 1, 'B': 1}, propensity_function="rate2*X")) + self.add_reaction(Reaction(name="reaction3", reactants={'X': 2, 'Y': 1}, products={'X': 3}, propensity_function="rate3*Y*X*(X-1)/2")) + self.add_reaction(Reaction(name="reaction4", reactants={'X': 1}, products={'D': 1}, propensity_function="rate4*X")) + + # Timespan + self.timespan(np.arange(0, 30, 0.01)) + + +class Degradation(Model): + def __init__(self): + Model.__init__(self, name="Degradation") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="decayrate", expression="0.05")) + + # Variables + self.add_species(Species(name="protein", initial_value=50, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="reaction", reactants={'protein': 1}, products={}, rate=self.listOfParameters["decayrate"])) + + # Timespan + self.timespan(np.arange(0, 100, 1)) + + +class Dimerization(Model): + def __init__(self): + Model.__init__(self, name="Dimerization") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="k_c", expression="0.005")) + self.add_parameter(Parameter(name="k_d", expression="0.08")) + + # Variables + self.add_species(Species(name="Monomer", initial_value=30, mode="discrete")) + self.add_species(Species(name="Dimer", initial_value=0, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="r_creation", reactants={'Monomer': 2}, products={'Dimer': 1}, rate=self.listOfParameters["k_c"])) + self.add_reaction(Reaction(name="r_dissociation", reactants={'Dimer': 1}, products={'Monomer': 2}, rate=self.listOfParameters["k_d"])) + + # Timespan + self.timespan(np.arange(0, 100, 1)) + + +class LotkavolterraOscillator(Model): + def __init__(self): + Model.__init__(self, name="Lotkavolterra_Oscillator") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="k1", expression="0.004")) + self.add_parameter(Parameter(name="k2", expression="0.5")) + self.add_parameter(Parameter(name="k3", expression="0.0045")) + + # Variables + self.add_species(Species(name="R", initial_value=0.0095, mode="continuous")) + self.add_species(Species(name="W", initial_value=0.008, mode="continuous")) + + # Reactions + self.add_reaction(Reaction(name="r1", reactants={'R': 1}, products={'R': 2}, rate=self.listOfParameters["k1"])) + self.add_reaction(Reaction(name="r2", reactants={'R': 1, 'W': 1}, products={'W': 2}, rate=self.listOfParameters["k2"])) + self.add_reaction(Reaction(name="r3", reactants={'W': 1}, products={}, rate=self.listOfParameters["k3"])) + + # Timespan + self.timespan(np.arange(0, 9000, 1)) + + +class MichaelisMenten(Model): + def __init__(self): + Model.__init__(self, name="Michaelis_Menten") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="rate1", expression="0.0017")) + self.add_parameter(Parameter(name="rate2", expression="0.5")) + self.add_parameter(Parameter(name="rate3", expression="0.1")) + + # Variables + self.add_species(Species(name="A", initial_value=301, mode="discrete")) + self.add_species(Species(name="B", initial_value=120, mode="discrete")) + self.add_species(Species(name="C", initial_value=0, mode="discrete")) + self.add_species(Species(name="D", initial_value=0, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="r1", reactants={'A': 1, 'B': 1}, products={'C': 1}, rate=self.listOfParameters["rate1"])) + self.add_reaction(Reaction(name="r2", reactants={'C': 1}, products={'A': 1, 'B': 1}, rate=self.listOfParameters["rate2"])) + self.add_reaction(Reaction(name="r3", reactants={'C': 1}, products={'B': 1, 'D': 1}, rate=self.listOfParameters["rate3"])) + + # Timespan + self.timespan(np.arange(0, 100, 1)) + + +class Opioid(Model): + def __init__(self): + Model.__init__(self, name="Opioid") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="alpha", expression="0.15")) + self.add_parameter(Parameter(name="epsilon", expression="0.8")) + self.add_parameter(Parameter(name="beta_p", expression="0.00266")) + self.add_parameter(Parameter(name="beta_a", expression="0.00094")) + self.add_parameter(Parameter(name="gamma", expression="0.00744")) + self.add_parameter(Parameter(name="zeta", expression="0.2")) + self.add_parameter(Parameter(name="delta", expression="0.1")) + self.add_parameter(Parameter(name="sigma", expression="0.9")) + self.add_parameter(Parameter(name="mu", expression="0.00729")) + self.add_parameter(Parameter(name="mu_prime", expression="0.01159")) + + # Variables + self.add_species(Species(name="Susceptible", initial_value=200, mode="discrete")) + self.add_species(Species(name="Prescribed_User", initial_value=0, mode="discrete")) + self.add_species(Species(name="Addicted", initial_value=0, mode="discrete")) + self.add_species(Species(name="Rehab", initial_value=0, mode="discrete")) + self.add_species(Species(name="Natural_Deaths", initial_value=0, mode="discrete")) + self.add_species(Species(name="Addicted_Deaths", initial_value=0, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="SP", reactants={'Susceptible': 1}, products={'Prescribed_User': 1}, rate=self.listOfParameters["alpha"])) + self.add_reaction(Reaction(name="SA_a", reactants={'Susceptible': 1}, products={'Addicted': 1}, rate=self.listOfParameters["beta_a"])) + self.add_reaction(Reaction(name="SA_p", reactants={'Susceptible': 1}, products={'Addicted': 1}, rate=self.listOfParameters["beta_p"])) + self.add_reaction(Reaction(name="PA", reactants={'Prescribed_User': 1}, products={'Addicted': 1}, rate=self.listOfParameters["gamma"])) + self.add_reaction(Reaction(name="PS", reactants={'Prescribed_User': 1}, products={'Susceptible': 1}, rate=self.listOfParameters["epsilon"])) + self.add_reaction(Reaction(name="AR", reactants={'Addicted': 1}, products={'Rehab': 1}, rate=self.listOfParameters["zeta"])) + self.add_reaction(Reaction(name="RA", reactants={'Rehab': 1}, products={'Addicted': 1}, rate=self.listOfParameters["delta"])) + self.add_reaction(Reaction(name="RS", reactants={'Rehab': 1}, products={'Susceptible': 1}, rate=self.listOfParameters["sigma"])) + self.add_reaction(Reaction(name="mu_S", reactants={'Susceptible': 1}, products={'Susceptible': 1, 'Natural_Deaths': 1}, rate=self.listOfParameters["mu"])) + self.add_reaction(Reaction(name="mu_P", reactants={'Prescribed_User': 1}, products={'Susceptible': 1, 'Natural_Deaths': 1}, rate=self.listOfParameters["mu"])) + self.add_reaction(Reaction(name="mu_R", reactants={'Rehab': 1}, products={'Susceptible': 1, 'Natural_Deaths': 1}, rate=self.listOfParameters["mu"])) + self.add_reaction(Reaction(name="mu_prime_A", reactants={'Addicted': 1}, products={'Susceptible': 1, 'Addicted_Deaths': 1}, rate=self.listOfParameters["mu_prime"])) + + # Timespan + self.timespan(np.arange(0, 200, 1)) + + +class Schlogl(Model): + def __init__(self): + Model.__init__(self, name="Schlogl") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="k1", expression="1")) + self.add_parameter(Parameter(name="k2", expression="1")) + + # Variables + self.add_species(Species(name="s1", initial_value=300, mode="discrete")) + self.add_species(Species(name="s2", initial_value=300, mode="discrete")) + self.add_species(Species(name="s3", initial_value=300, mode="discrete")) + self.add_species(Species(name="s4", initial_value=300, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="r1", reactants={'s1': 1, 's4': 1}, products={'s4': 2}, rate=self.listOfParameters["k1"])) + self.add_reaction(Reaction(name="r2", reactants={'s2': 1, 's4': 1}, products={'s3': 1}, rate=self.listOfParameters["k2"])) + + # Timespan + self.timespan(np.arange(0, 100000, 1000)) + + +class ToggleSwitch(Model): + def __init__(self): + Model.__init__(self, name="Toggle_Switch") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="alpha1", expression="10")) + self.add_parameter(Parameter(name="alpha2", expression="10")) + self.add_parameter(Parameter(name="beta", expression="2")) + self.add_parameter(Parameter(name="gamma", expression="2")) + self.add_parameter(Parameter(name="mu", expression="1")) + + # Variables + self.add_species(Species(name="A", initial_value=2, mode="discrete")) + self.add_species(Species(name="B", initial_value=2, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="cu", reactants={}, products={'A': 1}, propensity_function="alpha1/(1+pow(B, beta))")) + self.add_reaction(Reaction(name="cv", reactants={}, products={'B': 1}, propensity_function="alpha2/(1+pow(A, gamma))")) + self.add_reaction(Reaction(name="du", reactants={'A': 1}, products={}, rate=self.listOfParameters["mu"])) + self.add_reaction(Reaction(name="dv", reactants={'B': 1}, products={}, rate=self.listOfParameters["mu"])) + + # Timespan + self.timespan(np.arange(0, 250, 1)) + + +class VilarOscillator(Model): + def __init__(self): + Model.__init__(self, name="Vilar_Oscillator") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="alpha_a", expression="50")) + self.add_parameter(Parameter(name="alpha_a_prime", expression="500")) + self.add_parameter(Parameter(name="alpha_r", expression="0.01")) + self.add_parameter(Parameter(name="alpha_r_prime", expression="50")) + self.add_parameter(Parameter(name="beta_a", expression="50")) + self.add_parameter(Parameter(name="beta_r", expression="5")) + self.add_parameter(Parameter(name="delta_ma", expression="10")) + self.add_parameter(Parameter(name="delta_mr", expression="0.5")) + self.add_parameter(Parameter(name="delta_a", expression="1")) + self.add_parameter(Parameter(name="delta_r", expression="0.2")) + self.add_parameter(Parameter(name="gamma_a", expression="1")) + self.add_parameter(Parameter(name="gamma_r", expression="1")) + self.add_parameter(Parameter(name="gamma_c", expression="2")) + self.add_parameter(Parameter(name="theta_a", expression="50")) + self.add_parameter(Parameter(name="theta_r", expression="100")) + + # Variables + self.add_species(Species(name="Da", initial_value=1, mode="discrete")) + self.add_species(Species(name="Da_prime", initial_value=0, mode="discrete")) + self.add_species(Species(name="Ma", initial_value=0, mode="discrete")) + self.add_species(Species(name="Dr", initial_value=1, mode="discrete")) + self.add_species(Species(name="Dr_prime", initial_value=0, mode="discrete")) + self.add_species(Species(name="Mr", initial_value=0, mode="discrete")) + self.add_species(Species(name="C", initial_value=10, mode="discrete")) + self.add_species(Species(name="A", initial_value=10, mode="discrete")) + self.add_species(Species(name="R", initial_value=10, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="r1", reactants={'Da_prime': 1}, products={'Da': 1}, rate=self.listOfParameters["theta_a"])) + self.add_reaction(Reaction(name="r2", reactants={'Da': 1, 'A': 1}, products={'Da_prime': 1}, rate=self.listOfParameters["gamma_a"])) + self.add_reaction(Reaction(name="r3", reactants={'Dr_prime': 1}, products={'Dr': 1}, rate=self.listOfParameters["theta_r"])) + self.add_reaction(Reaction(name="r4", reactants={'Dr': 1, 'A': 1}, products={'Dr_prime': 1}, rate=self.listOfParameters["gamma_r"])) + self.add_reaction(Reaction(name="r5", reactants={'Da_prime': 1}, products={'Da_prime': 1, 'Ma': 1}, rate=self.listOfParameters["alpha_a_prime"])) + self.add_reaction(Reaction(name="r6", reactants={'Da': 1}, products={'Da': 1, 'Ma': 1}, rate=self.listOfParameters["alpha_a"])) + self.add_reaction(Reaction(name="r7", reactants={'Ma': 1}, products={}, rate=self.listOfParameters["delta_ma"])) + self.add_reaction(Reaction(name="r8", reactants={'Ma': 1}, products={'A': 1, 'Ma': 1}, rate=self.listOfParameters["beta_a"])) + self.add_reaction(Reaction(name="r9", reactants={'Da_prime': 1}, products={'Da_prime': 1, 'A': 1}, rate=self.listOfParameters["theta_a"])) + self.add_reaction(Reaction(name="r10", reactants={'Dr_prime': 1}, products={'Dr_prime': 1, 'A': 1}, rate=self.listOfParameters["theta_a"])) + self.add_reaction(Reaction(name="r11", reactants={'A': 1}, products={}, rate=self.listOfParameters["gamma_c"])) + self.add_reaction(Reaction(name="r12", reactants={'A': 1, 'R': 1}, products={'C': 1}, rate=self.listOfParameters["gamma_c"])) + self.add_reaction(Reaction(name="r13", reactants={'Dr_prime': 1}, products={'Dr_prime': 1, 'Mr': 1}, rate=self.listOfParameters["alpha_r_prime"])) + self.add_reaction(Reaction(name="r14", reactants={'Dr': 1}, products={'Dr': 1, 'Mr': 1}, rate=self.listOfParameters["alpha_r"])) + self.add_reaction(Reaction(name="r15", reactants={'Mr': 1}, products={}, rate=self.listOfParameters["delta_mr"])) + self.add_reaction(Reaction(name="r16", reactants={'Mr': 1}, products={'Mr': 1, 'R': 1}, rate=self.listOfParameters["beta_r"])) + self.add_reaction(Reaction(name="r17", reactants={'R': 1}, products={}, rate=self.listOfParameters["delta_r"])) + self.add_reaction(Reaction(name="r18", reactants={'C': 1}, products={'R': 1}, rate=self.listOfParameters["delta_a"])) + + # Timespan + self.timespan(np.arange(0, 200, 1)) + + +class Oregonator(Model): + def __init__(self): + Model.__init__(self, name="Oregonator") + self.volume = 1 + + # Parameters + self.add_parameter(Parameter(name="k1", expression="2")) + self.add_parameter(Parameter(name="k2", expression="0.1")) + self.add_parameter(Parameter(name="k3", expression="104")) + self.add_parameter(Parameter(name="k4", expression="4e-07")) + self.add_parameter(Parameter(name="k5", expression="26")) + + # Variables + self.add_species(Species(name="F", initial_value=2, mode="continuous")) + self.add_species(Species(name="A", initial_value=250, mode="continuous")) + self.add_species(Species(name="B", initial_value=500, mode="continuous")) + self.add_species(Species(name="C", initial_value=1000, mode="continuous")) + self.add_species(Species(name="P", initial_value=0, mode="continuous")) + + # Reactions + self.add_reaction(Reaction(name="reaction1", reactants={'B': 1, 'F': 1}, products={'A': 1, 'F': 1}, rate=self.listOfParameters["k1"])) + self.add_reaction(Reaction(name="reaction2", reactants={'A': 1, 'B': 1}, products={'P': 1}, rate=self.listOfParameters["k2"])) + self.add_reaction(Reaction(name="reaction3", reactants={'A': 1, 'F': 1}, products={'A': 2, 'C': 1, 'F': 1}, rate=self.listOfParameters["k3"])) + self.add_reaction(Reaction(name="reaction4", reactants={'A': 2}, products={'P': 1}, rate=self.listOfParameters["k4"])) + self.add_reaction(Reaction(name="reaction5", reactants={'C': 1, 'F': 1}, products={'B': 1, 'F': 1}, rate=self.listOfParameters["k5"])) + + # Timespan + self.timespan(np.arange(0, 5, 0.1)) + + +class TysonOscillator(Model): + def __init__(self): + Model.__init__(self, name="Tyson_Oscillator") + self.volume = 300 + + # Parameters + self.add_parameter(Parameter(name="P", expression="2")) + self.add_parameter(Parameter(name="kt", expression="20")) + self.add_parameter(Parameter(name="kd", expression="1")) + self.add_parameter(Parameter(name="a0", expression="0.005")) + self.add_parameter(Parameter(name="a1", expression="0.05")) + self.add_parameter(Parameter(name="a2", expression="0.1")) + self.add_parameter(Parameter(name="kdx", expression="1")) + + # Variables + self.add_species(Species(name="X", initial_value=197, mode="discrete")) + self.add_species(Species(name="Y", initial_value=255, mode="discrete")) + + # Reactions + self.add_reaction(Reaction(name="rxn1", reactants={}, products={'X': 1}, propensity_function="vol*1/(1+(Y*Y/((vol*vol))))")) + self.add_reaction(Reaction(name="rxn2", reactants={'X': 1}, products={}, rate=self.listOfParameters["kdx"])) + self.add_reaction(Reaction(name="rxn3", reactants={'X': 1}, products={'X': 1, 'Y': 1}, rate=self.listOfParameters["kt"])) + self.add_reaction(Reaction(name="rxn4", reactants={'Y': 1}, products={}, rate=self.listOfParameters["kd"])) + self.add_reaction(Reaction(name="rxn5", reactants={'Y': 1}, products={}, propensity_function="Y/(a0 + a1*(Y/vol)+a2*Y*Y/(vol*vol))")) + + # Timespan + self.timespan(np.arange(0, 100, 1)) From 3169bfb2a2e937e7117f621efec74193634ddaac Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 1 Jul 2021 12:45:47 -0400 Subject: [PATCH 132/133] Refactored the test to use the models in the new location. --- stochss/tests/test_gillespy2.py | 171 ++++++++++++++++---------------- 1 file changed, 88 insertions(+), 83 deletions(-) diff --git a/stochss/tests/test_gillespy2.py b/stochss/tests/test_gillespy2.py index 636f356ff7..b16fbfa94a 100644 --- a/stochss/tests/test_gillespy2.py +++ b/stochss/tests/test_gillespy2.py @@ -21,142 +21,147 @@ from gillespy2 import GillesPySolver -from stochss.handlers import StochSSModel +from example_models import ( + Brusselator, + Degradation, + Dimerization, + LotkavolterraOscillator, + MichaelisMenten, + Opioid, + Schlogl, + ToggleSwitch, + VilarOscillator, + Oregonator, + TysonOscillator +) os.chdir('/stochss') -class TestModelTemplate(unittest.TestCase): +# pylint: disable=import-outside-toplevel +class TestGillesPy2Dependency(unittest.TestCase): ''' ################################################################################################ Unit tests for GillesPy2 dependency. ################################################################################################ ''' def setUp(self): - ''' Create a list of common example paths for each test. ''' - self.model_paths = ["Brusselator/Brusselator.mdl", - "Degradation/Degradation.mdl", - "Dimerization/Dimerization.mdl", - "Lotkavolterra_Oscillator/Lotkavolterra_Oscillator.mdl", - "Michaelis_Menten/Michaelis_Menten.mdl", - "Opioid/Opioid.mdl", - "Schlogl/Schlogl.mdl", - "Toggle_Switch/Toggle_Switch.mdl", - "Vilar_Oscillator/Vilar_Oscillator.mdl" - ] - - ################################################################################################ + ''' Create a list of common example paths for each test. ''' + self.test_models = [Brusselator, Degradation, Dimerization, LotkavolterraOscillator, + MichaelisMenten, Opioid, Schlogl, ToggleSwitch, VilarOscillator] + + ################################################################################################ # Unit tests for GillesPy2 dependency check_cpp_support. ################################################################################################ def test_check_cpp_support(self): - ''' Check if the check cpp support functions works in StochSS. ''' - from gillespy2.solvers.utilities.cpp_support_test import check_cpp_support + ''' Check if the check cpp support functions works in StochSS. ''' + from gillespy2.solvers.utilities.cpp_support_test import check_cpp_support - self.assertIsInstance(check_cpp_support(), bool) + self.assertIsInstance(check_cpp_support(), bool) ################################################################################################ # Unit tests for GillesPy2 dependency get_best_solver. ################################################################################################ def test_get_best_solver(self): - ''' Check if the get best solver function works in StochSS. ''' - test_model = StochSSModel(path=os.path.join("Examples", self.model_paths[0])).convert_to_gillespy2() - test_solver = test_model.get_best_solver() - self.assertIsInstance(test_solver(), GillesPySolver) + ''' Check if the get best solver function works in StochSS. ''' + test_model = self.test_models[0]() + test_solver = test_model.get_best_solver() + self.assertIsInstance(test_solver(), GillesPySolver) ################################################################################################ # Unit tests for GillesPy2 dependency get_best_solver_algo. ################################################################################################ def test_get_best_solver_algo(self): - ''' Check if the get best solver algo function works in StochSS. ''' - test_algos = ["ODE", "SSA", "Tau-Leaping"] - test_model = StochSSModel(path=os.path.join("Examples", self.model_paths[0])).convert_to_gillespy2() - for test_algo in test_algos: - with self.subTest(test_algo=test_algo): - test_solver = test_model.get_best_solver_algo(algorithm=test_algo) - self.assertIsInstance(test_solver(), GillesPySolver) + ''' Check if the get best solver algo function works in StochSS. ''' + test_algos = ["ODE", "SSA", "Tau-Leaping"] + test_model = self.test_models[0]() + for test_algo in test_algos: + with self.subTest(test_algo=test_algo): + test_solver = test_model.get_best_solver_algo(algorithm=test_algo) + self.assertIsInstance(test_solver(), GillesPySolver) ################################################################################################ # Unit tests for GillesPy2 dependency solvers. ################################################################################################ def test_ode_solver(self): - ''' Check if the test_models run with the ODESolver. ''' - from gillespy2 import ODESolver + ''' Check if the test_models run with the ODESolver. ''' + from gillespy2 import ODESolver - self.model_paths.append("Oregonator/Oregonator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_model.run(solver=ODESolver) + self.test_models.append(Oregonator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_model.run(solver=ODESolver) def test_ode_c_solver(self): - ''' Check if the test_models run with the ODECSolver. ''' - from gillespy2 import ODECSolver + ''' Check if the test_models run with the ODECSolver. ''' + from gillespy2 import ODECSolver - self.model_paths.append("Oregonator/Oregonator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_solver = ODECSolver(model=test_model) - test_model.run(solver=test_solver) + self.test_models.append(Oregonator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_solver = ODECSolver(model=test_model) + test_model.run(solver=test_solver) def test_numpy_ssa_solver(self): - ''' Check if the test_models run with the NumPySSASolver. ''' - from gillespy2 import NumPySSASolver + ''' Check if the test_models run with the NumPySSASolver. ''' + from gillespy2 import NumPySSASolver - self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_model.run(solver=NumPySSASolver) + self.test_models.append(TysonOscillator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_model.run(solver=NumPySSASolver) def test_ssa_c_solver(self): - ''' Check if the test_models run with the SSACSolver. ''' - from gillespy2 import SSACSolver + ''' Check if the test_models run with the SSACSolver. ''' + from gillespy2 import SSACSolver - self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_solver = SSACSolver(model=test_model) - test_model.run(solver=test_solver) + self.test_models.append(TysonOscillator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_solver = SSACSolver(model=test_model) + test_model.run(solver=test_solver) def test_tau_leaping_solver(self): - ''' Check if the test_models run with the TauLeapingSolver. ''' - from gillespy2 import TauLeapingSolver + ''' Check if the test_models run with the TauLeapingSolver. ''' + from gillespy2 import TauLeapingSolver - self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_model.run(solver=TauLeapingSolver) + self.test_models.append(TysonOscillator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_model.run(solver=TauLeapingSolver) def test_tau_leaping_c_solver(self): - ''' Check if the test_models run with the TauLeapingCSolver. ''' - from gillespy2 import TauLeapingCSolver + ''' Check if the test_models run with the TauLeapingCSolver. ''' + from gillespy2 import TauLeapingCSolver - self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_solver = TauLeapingCSolver(model=test_model) - test_model.run(solver=test_solver) + self.test_models.append(TysonOscillator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_solver = TauLeapingCSolver(model=test_model) + test_model.run(solver=test_solver) def test_tau_hybrid_solver(self): - ''' Check if the test_models run with the TauHybridSolver. ''' - from gillespy2 import TauHybridSolver - - self.model_paths.append("Oregonator/Oregonator.mdl") - self.model_paths.append("Tyson_Oscillator/Tyson_Oscillator.mdl") - for model_path in self.model_paths: - with self.subTest(model=model_path.split('/').pop()): - test_model = StochSSModel(path=os.path.join("Examples", model_path)).convert_to_gillespy2() - test_model.run(solver=TauHybridSolver) + ''' Check if the test_models run with the TauHybridSolver. ''' + from gillespy2 import TauHybridSolver + + self.test_models.append(Oregonator) + self.test_models.append(TysonOscillator) + for model in self.test_models: + test_model = model() + with self.subTest(model=test_model.name): + test_model.run(solver=TauHybridSolver) From 306c27baee0095c338cb44f70c51187cd15e2070 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Thu, 1 Jul 2021 12:46:53 -0400 Subject: [PATCH 133/133] Added pylint decorators to ignore warnings for tests. --- stochss/tests/test_stochss_base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stochss/tests/test_stochss_base.py b/stochss/tests/test_stochss_base.py index 81c5c979d3..11e2f1f920 100644 --- a/stochss/tests/test_stochss_base.py +++ b/stochss/tests/test_stochss_base.py @@ -33,6 +33,8 @@ os.chdir('/stochss') +# pylint: disable=too-many-public-methods +# pylint: disable=line-too-long class TestStochSSBaseObject(unittest.TestCase): ''' ################################################################################################