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/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) \ 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 c55db1f6b8..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 @@ -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) }, diff --git a/client/views/view-domain-types.js b/client/models/boundary-condition.js similarity index 74% rename from client/views/view-domain-types.js rename to client/models/boundary-condition.js index 5b2d8906fa..d8e893bfec 100644 --- a/client/views/view-domain-types.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 @@ -16,11 +16,14 @@ 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'); +//models +var State = require('ampersand-state'); -module.exports = View.extend({ - template: template, +module.exports = State.extend({ + props: { + compID: 'number', + name: 'string', + expression: 'string', + annotation: 'string' + } }); \ No newline at end of file diff --git a/client/views/view-rules.js b/client/models/boundary-conditions.js similarity index 58% rename from client/views/view-rules.js rename to client/models/boundary-conditions.js index c4939f45d4..667af7cd85 100644 --- a/client/views/view-rules.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 @@ -16,11 +16,20 @@ 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'); +//collections +var Collection = require('ampersand-collection'); +//models +var BoundaryCondition = require('./boundary-condition'); -module.exports = View.extend({ - template: template, +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/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..97c03bbec8 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 @@ -23,11 +23,12 @@ var Specie = require('./specie'); module.exports = State.extend({ props: { icType: 'string', - count: 'number', + annotation: 'string', + count: 'any', types: 'object', - x: 'number', - y: 'number', - z: 'number', + x: 'any', + y: 'any', + z: 'any', }, children: { specie: Specie, diff --git a/client/models/initial-conditions.js b/client/models/initial-conditions.js index c6984f1a16..72b22b87eb 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 @@ -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/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 b8b59ab441..9799e6e4f2 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 @@ -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 @@ -30,6 +32,7 @@ var Reactions = require('./reactions'); var Rules = require('./rules'); var Events = require('./events'); var FunctionDefinitions = require('./function-definitions'); +var BoundaryConditions = require('./boundary-conditions'); module.exports = Model.extend({ url: function () { @@ -49,7 +52,8 @@ module.exports = Model.extend({ reactions: Reactions, rules: Rules, eventsCollection: Events, - functionDefinitions: FunctionDefinitions + functionDefinitions: FunctionDefinitions, + boundaryConditions: BoundaryConditions }, children: { modelSettings: TimespanSettings, @@ -91,12 +95,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; } @@ -124,7 +128,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: _.bind(self.autoSave, self) }); + }, 120000); }, //called when save button is clicked saveModel: function (cb=null) { 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 5ba9377b71..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 @@ -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; } 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 98ab1d7879..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 @@ -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) 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 bdb85123e5..dea04098a6 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 @@ -23,26 +23,23 @@ 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'); 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'); 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'); 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'); @@ -54,9 +51,13 @@ 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(); }, + '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', @@ -66,6 +67,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'); @@ -218,6 +220,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, @@ -231,10 +239,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"); + $(this.queryByHook("system-volume-container")).css("display", "none"); + $(this.queryByHook("advaced-model-mode")).css("display", "none"); $(this.queryByHook("spatial-beta-message")).css("display", "block"); this.renderDomainViewer(); this.renderInitialConditions(); + this.renderBoundaryConditionsView(); $(this.queryByHook("toggle-preview-domain")).css("display", "inline-block"); this.openDomainPlot(); }else { @@ -242,14 +252,14 @@ 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'); } this.renderSystemVolumeView(); } - $(document).ready(function () { + this.model.autoSave(); + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -259,6 +269,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() @@ -287,33 +306,26 @@ 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, + defaultMode: this.model.defaultMode + }); 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() @@ -321,37 +333,25 @@ let ModelEditor = PageView.extend({ this.parametersEditor = new ParametersEditorView({collection: this.model.parameters}); 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) { + 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}); - } + this.eventsEditor = new EventsEditorView({collection: this.model.eventsCollection}); 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 () { @@ -368,10 +368,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 +426,66 @@ 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.renderEditSpeciesView(); + this.speciesEditor.renderViewSpeciesView(); + } + }, + setDefaultMode: function (e) { + 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"); + } + } + }, + updateVolumeViewer: function (e) { + $(this.queryByHook("view-volume")).html("Volume: " + this.model.volume) } }); 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 29403e2122..64935dff14 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 @@ -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; } @@ -697,3 +702,7 @@ span.checkbox { overflow-y: hidden; white-space: nowrap; } + +.card-header { + background-color: rgba(0, 0, 0, 0.1); +} diff --git a/client/templates/includes/boundaryConditionsEditor.pug b/client/templates/includes/boundaryConditionsEditor.pug new file mode 100644 index 0000000000..fea9fc7d4b --- /dev/null +++ b/client/templates/includes/boundaryConditionsEditor.pug @@ -0,0 +1,133 @@ +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-bc") + + + div.card-body + + p.mb-0 + | Set spatial regions of the domain where a property of particles are held constant. + + div.collapse.tab-content(id="collapse-boundary-conditions") + + div.tab-pane.active(id="edit-boundary-conditions" data-hook="edit-boundary-conditions") + + 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-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-4: h6 Target + + 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") Property/Variable + td(data-hook="new-bc-target") + tr + th(scope="row") 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 + input(type="checkbox" checked=this.newBC.deterministic data-hook="new-bc-deterministic" disabled) + + div.col-sm-6 + + 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") + + 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") + + hr + + div.mx-1.row.head + + div.col-sm-3: h6 Name + + div.col-sm-7: h6 Expression + + div.col-sm-2(data-hook="bc-annotation-header") + + h6 Annotation + + div.mt-3(data-hook="view-boundary-conditions-list") diff --git a/client/templates/includes/domainTypesViewer.pug b/client/templates/includes/domainTypesViewer.pug deleted file mode 100644 index fab182ab9e..0000000000 --- a/client/templates/includes/domainTypesViewer.pug +++ /dev/null @@ -1,13 +0,0 @@ -tr - - td=this.model.name - - td=this.model.numParticles - - td=this.model.mass - - td=this.model.volume - - td=this.model.nu - - td: input(type="checkbox" checked=this.model.fixed disabled) \ No newline at end of file diff --git a/client/templates/includes/domainViewer.pug b/client/templates/includes/domainViewer.pug index 296b7a0d7f..14590cd722 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.component-valid(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 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.my-3(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-btn") 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") 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/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/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/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/editInitialCondition.pug b/client/templates/includes/editInitialCondition.pug index 51d9602e52..3b29326c07 100644 --- a/client/templates/includes/editInitialCondition.pug +++ b/client/templates/includes/editInitialCondition.pug @@ -1,21 +1,36 @@ -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-2(data-hook="count-container") + + div.col-sm-2 + + 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 + + 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(data-hook="remove") X 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/editRule.pug b/client/templates/includes/editRule.pug index 30c7547445..cdbb34eaeb 100644 --- a/client/templates/includes/editRule.pug +++ b/client/templates/includes/editRule.pug @@ -1,17 +1,32 @@ -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 - td: div(data-hook="rule-expression") + div.pl-2(data-hook="rule-name") - td + 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") + div(data-hook="rule-type") - 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(data-hook="rule-variable") + + div.col-sm-2 + + div(data-hook="rule-expression") + + 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/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/editSpecies.pug b/client/templates/includes/editSpecies.pug new file mode 100644 index 0000000000..3c1ad72082 --- /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.align-items-baseline + + h6.inline 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)) + + 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/eventAssignmentsEditor.pug b/client/templates/includes/eventAssignmentsEditor.pug index 022cef0cd7..f32e4cf6da 100644 --- a/client/templates/includes/eventAssignmentsEditor.pug +++ b/client/templates/includes/eventAssignmentsEditor.pug @@ -1,26 +1,55 @@ -div +div#event-assignments-editor - table.table + div(data-hook="event-assignments-header") - 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.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-assignments") - - th(scope="col") - div - div.inline Expression + div - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.parent.parent.tooltips.assignmentExpression) + div.collapse.show.tab-content(id="collapse-event-assignments" data-hook="event-assignments-list-container") - th(scope="col") Remove + div.tab-pane.active(id="edit-event-assignments" data-hook="edit-event-assignments") - tbody(data-hook="event-assignments-container") + hr - button.btn.btn-outline-primary.box-shadow(data-hook="add-event-assignment", type="button") - | Add Assignment \ No newline at end of file + div.mx-1.row.head.align-items-baseline + + div.col-sm-3 + + div.inline Target + + 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.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/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/eventDetails.pug b/client/templates/includes/eventDetails.pug index 6ccdb91b55..c473f51381 100644 --- a/client/templates/includes/eventDetails.pug +++ b/client/templates/includes/eventDetails.pug @@ -1,14 +1,18 @@ 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 + 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') + @@ -27,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) @@ -61,18 +65,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 - - 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 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..0828d3d9ee 100644 --- a/client/templates/includes/eventsEditor.pug +++ b/client/templates/includes/eventsEditor.pug @@ -1,45 +1,90 @@ -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.align-items-baseline + + div.col-md-6.container-part + + hr + + div.mx-1.row.head.align-items-baseline + + div.col-md-2 + + h6 Edit + + div.col-md-5 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-md-3 + + h6.inline Annotation + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + + div.col-md-2 + + h6 Remove + + div.mt-3(data-hook="edit-event-listing-container") + + div.col-md-6.container-part(data-hook="event-details-container") - div.row + button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event - div.col-md-6.container-part - p - | A discontinuous action triggered by the state of the system. + div.tab-pane(id="view-events" data-hook="view-events") - table.table + hr - thead + div.mx-1.row.head - tr + div.col-sm-2 - th(scope="col") Edit + h6 Name - 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 Trigger - th(scope="col") - div - div.inline Annotation + div.col-sm-4 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + h6 Assignments - th(scope="col") Remove + div.col-sm-2 - tbody(data-hook="event-listing-container") + h6 Advanced - div.col-md-6.container-part(data-hook="event-details-container") + div.col-sm-2(data-hook="events-annotation-header") - button.btn.btn-outline-primary.box-shadow(data-hook="add-event") Add Event + h6 Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-events") Save Events + div.mt-3(data-hook="view-event-listing-container") 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/templates/includes/initialConditionsEditor.pug b/client/templates/includes/initialConditionsEditor.pug index 935e31f578..bac19f8d8c 100644 --- a/client/templates/includes/initialConditionsEditor.pug +++ b/client/templates/includes/initialConditionsEditor.pug @@ -1,51 +1,86 @@ -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 + + li.nav-item + + a.nav-link.tab(data-hook="initial-conditions-view-tab" data-toggle="tab" href="#view-initial-conditions") View - div.collapse(id="initial-condition" data-hook="initial-conditions") + button.btn.btn-outline-collapse(data-toggle="collapse" data-target="#initial-condition" data-hook="initial-condition-button") + - p + 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-2: h6 Count + + div.col-sm-2: h6 Details + + 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 + + div.my-3(data-hook="edit-initial-conditions-collection") + + div.dropdown.inline + + 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 + + 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 + + div.tab-pane(id="view-initial-conditions" data-hook="view-initial-conditions") + + hr - thead - - tr - - th(scope="col") Type - - th(scope="col") Variable + div.mx-1.row.head.align-items-baseline - th(scope="col") Count - - th(scope="col") Details - - th(scope="col") Remove + div.col-sm-2: h6 Type - tbody(data-hook="initial-conditions-collection") + div.col-sm-2: h6 Variable - div.dropdown.inline + div.col-sm-2: h6 Count - 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.col-sm-4: h6 Details - 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 + div.col-sm-2(data-hook="initial-conditions-annotation-header"): h6 Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-initial-conditions") Save Initial Conditions \ No newline at end of file + div.my-3(data-hook="view-initial-conditions-collection") 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..c3b6e5b7ef 100644 --- a/client/templates/includes/reactionsEditor.pug +++ b/client/templates/includes/reactionsEditor.pug @@ -1,78 +1,117 @@ -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 + li.nav-item + + a.nav-link.tab.active(data-hook="reactions-edit-tab" data-toggle="tab" href="#edit-reactions") Edit + + 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 + + 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. - div.row - div.col-md-7.container-part: table.table - thead: tr - th(scope="col") Edit - th(scope="col") - div - div.inline Name + div.collapse.tab-content(id="collapse-reaction" data-hook="reactions-list-container") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - - th(scope="col") Summary - th(scope="col") - div - div.inline Annotation + div.tab-pane.active(id="edit-reactions" data-hook="edit-reactions") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.row.mb-3 + + div.col-md-7.container-part + + hr - th(scope="col") Remove + div.mx-1.row.head.align-items-baseline + + div.col-md-2: h6.align-bottom Edit + + div.col-md-2 - tbody(data-hook="reaction-list") + h6.inline Name - div.col-md-5.container-part(data-hook="reaction-details-container") + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) - 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.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 + + div.mt-3(data-hook="edit-reaction-list") + + 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.component-valid(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 + + div.tab-pane(id="view-reactions" data-hook="view-reactions") + + hr + + 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/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/templates/includes/ruleEditor.pug b/client/templates/includes/ruleEditor.pug index 7065291d4e..97cfd2c051 100644 --- a/client/templates/includes/ruleEditor.pug +++ b/client/templates/includes/ruleEditor.pug @@ -1,67 +1,109 @@ -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 + + h6.inline Target + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) + + div.col-sm-2 + + h6.inline Formula + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + + 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 - thead + div.my-3(data-hook="edit-rule-list-container") - tr + div.dropdown - th(scope="col") - div - div.inline Name + 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 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + 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 - th(scope="col") - div - div.inline Type + div.tab-pane(id="view-rules" data-hook="view-rules") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.type) + hr - th(scope="col") - div - div.inline Target + div.mx-1.row.head.align-items-baseline - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.variable) + div.col-sm-2 - th(scope="col") - div - div.inline Formula + h6.inline Name - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.expression) + div.col-sm-2 - th(scope="col") - div - div.inline Annotation + h6.inline Type - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + div.col-sm-2 - th(scope="col") Remove + h6.inline Target - tbody(data-hook="rule-list-container") + div.col-sm-4 - div.dropdown.inline + h6.inline Formula - 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 + div.col-sm-2(data-hook="rules-annotation-header") - 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 + h6.inline Annotation - button.btn.btn-outline-primary.box-shadow.ml-2(data-hook="save-rules") Save Rules + div.mt-3(data-hook="view-rules-list-container") 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/templates/includes/sbmlComponentEditor.pug b/client/templates/includes/sbmlComponentEditor.pug index 7d4ec4423d..84ac3d3fb5 100644 --- a/client/templates/includes/sbmlComponentEditor.pug +++ b/client/templates/includes/sbmlComponentEditor.pug @@ -1,34 +1,75 @@ -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#function-definitions.card + + div.card-header.pb-0 + + h5.inline.mr-3 Function Definitions + + div.inline.mr-3 + + ul.nav.nav-tabs.card-header-tabs(id="function-definitions-tabs") + + 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) + + div.col-sm-2 + + h6 Remove + + div.my-3(data-hook="edit-function-definition-list") + + div.tab-pane(id="view-function-definitions" data-hook="view-function-definitions") - h5.inline Function Definitions - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-function-definitions", data-hook="collapse-function-definitions") - + hr - div.collapse(class="show", id="collapse-function-definitions") + div.mx-1.row.head.align-items-baseline - table.table + div.col-sm-10 - thead - - tr - - th(scope="col") Signature + h6 Signature - th(scope="col") - div - div.inline Annotation + div.col-sm-2(data-hook="function-definition-annotation-header") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) + h6.inline Annotation - if(!this.viewMode) - th(scope="col") Remove - - tbody(data-hook="function-definition-list") \ No newline at end of file + div.my-3(data-hook="view-function-definition-list") diff --git a/client/templates/includes/spatialSpeciesEditor.pug b/client/templates/includes/spatialSpeciesEditor.pug index 4adc51dfcb..ae24b9fc88 100644 --- a/client/templates/includes/spatialSpeciesEditor.pug +++ b/client/templates/includes/spatialSpeciesEditor.pug @@ -1,45 +1,85 @@ -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") + + 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/includes/speciesEditor.pug b/client/templates/includes/speciesEditor.pug index 9162e167f8..0c0ae48995 100644 --- a/client/templates/includes/speciesEditor.pug +++ b/client/templates/includes/speciesEditor.pug @@ -1,92 +1,89 @@ -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 + 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 + + 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") - div - div.inline Name + 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-3 + + h6.inline Name + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + + div.col-sm-5 + + h6.inline Initial Condition + + div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.initialValue) + + div.col-sm-2 - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.name) + h6.inline Annotation - th(scope="col") - div - div.inline Initial Condition + 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.initialValue) + div.col-sm-2 - th(scope="col") - div - div.inline Annotation + h6.inline Remove - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.annotation) - - th(scope="col") - div - div.inline Remove + 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.remove) - - tbody(data-hook="specie-list") + div.my-3(data-hook="edit-specie-list") - table.table - thead - tr - th(scope="col", colspan="3") + div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable - div + button.btn.btn-outline-primary.box-shadow(data-hook="add-species") Add Variable - div.inline Variable Mode + div.tab-pane(id="view-species" data-hook="view-species") - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.speciesMode) + hr - 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.mx-1.row.head.align-items-baseline - table.table + div.col-sm-2 - thead + h6.inline Name - tr + div.col-sm-2 - th(scope="col") Name + h6.inline Initial Condition - th(scope="col") - div - div.inline Mode + div.col-sm-2 - 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) + h6.inline Mode - div.tooltip-icon(data-html="true" data-toggle="tooltip" title=this.tooltips.switchValue) + div.col-sm-4(data-hook="species-switching-header") - tbody(data-hook="edit-species-mode") + h6.inline Switch Tolerance/Minimum Value for Switching - div(data-hook="species-collection-error"): p.text-danger A model must have at least one variable + 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 + div.my-3(data-hook="view-specie-list") 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/templates/includes/timespanSettings.pug b/client/templates/includes/timespanSettings.pug index 35e7ad3b88..e49fe14c96 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-1 + + span.inline 1 + + div.col-sm-8 + + span.mr-2.inline Current Value: + + div.inline(data-hook="timestep-size-value") + + div.col-sm-3.d-flex.justify-content-end + + span.inline 1e-5 + + input.custom-range( + type="range" + min="0" + max="5" + step="1" + value=this.tssValue + 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/templates/includes/viewBoundaryCondition.pug b/client/templates/includes/viewBoundaryCondition.pug new file mode 100644 index 0000000000..6ea4db586d --- /dev/null +++ b/client/templates/includes/viewBoundaryCondition.pug @@ -0,0 +1,20 @@ +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 + + 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/templates/includes/viewDomainType.pug b/client/templates/includes/viewDomainType.pug new file mode 100644 index 0000000000..1baecacecc --- /dev/null +++ b/client/templates/includes/viewDomainType.pug @@ -0,0 +1,22 @@ +div.mx-1 + + if(this.model.collection.indexOf(this.model) !== 1) + hr + + div.row + + div.col-sm-2 + + div.pl-2=this.model.name + + div.col-sm-2=this.model.numParticles + + 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/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/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/templates/includes/viewEvents.pug b/client/templates/includes/viewEvents.pug index a26dfb151f..ef20893766 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="view-use-values-from" data-hook="view-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="view-use-values-from" data-hook="view-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/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/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") 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) 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") 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/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/templates/pages/modelEditor.pug b/client/templates/pages/modelEditor.pug index eeb3660d85..1c1d3cc683 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(data-hook="advaced-model-mode") + 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") @@ -36,9 +56,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 +66,54 @@ 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="rules-editor-container") + div(data-hook="events-editor-container") - div(data-hook="sbml-component-container") + div(data-hook="rules-editor-container") - div.card.card-body.collapse.show(data-hook="system-volume-container") + div(data-hook="boundary-conditions-container") - div + div(data-hook="sbml-component-container") - h3.inline System Volume - button.btn.btn-outline-collapse(data-toggle="collapse", data-target="#collapse-system-volume", data-hook="collapse-system-volume") + + div.card.collapse.show(data-hook="system-volume-container") - 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.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.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.collapse.tab-content(id="collapse-system-volume" data-hook="system-volume-section") + + div.tab-pane.active(id="edit-system-volume" data-hook="edit-system-volume") + + hr + + div(data-hook="edit-volume") + + 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") diff --git a/client/tooltips.js b/client/tooltips.js index 4c426230a2..56fc32971c 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 @@ -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.", @@ -39,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.", @@ -178,5 +182,8 @@ module.exports = { pressure: "Atmospheric or background pressure.", speed: "Approximate or artificial speed of sound" + }, + boundaryConditionsEditor: { + annotation: "An optional note about a boundary condition." } } 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 new file mode 100644 index 0000000000..a91cf1b9fb --- /dev/null +++ b/client/views/boundary-condition-view.js @@ -0,0 +1,86 @@ +/* +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 . +*/ + +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.renderEditBoundaryConditionView(); + self.parent.renderViewBoundaryConditionView(); + 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 diff --git a/client/views/boundary-conditions-editor.js b/client/views/boundary-conditions-editor.js new file mode 100644 index 0000000000..bfc9607a00 --- /dev/null +++ b/client/views/boundary-conditions-editor.js @@ -0,0 +1,394 @@ +/* +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 . +*/ + +let $ = require('jquery'); +let path = require('path'); +//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 SelectView = require('ampersand-select-view'); +let BoundaryConditionView = require('./boundary-condition-view'); +//templates +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-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-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', + '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); + 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'); + }else{ + this.renderEditBoundaryConditionView(); + this.toggleAddNewBCButton(); + } + this.renderViewBoundaryConditionView(); + }, + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); + }, + handleAddBCClick: function (e) { + let endpoint = path.join(app.getApiPath(), "model/new-bc") + let self = this; + if(this.newBC.property === "v") { + this.newBC.value = this.newBCVector + } + let data = {model_path: this.collection.parent.directory, + kwargs: this.newBC} + app.postXHR(endpoint, data, { + 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; + }, + handleSetTarget: function (e) { + 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 = species; + $(this.queryByHook("new-bc-deterministic")).prop("disabled", false); + } + }, + handleSetValue: function (e) { + let key = e.delegateTarget.dataset.target; + let value = e.target.value; + if(key === "name") { + this.newBCName = value; + }else{ + if(key.endsWith("min") || key.endsWith("max") || key === "type_id"){ + 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.toggleAddNewBCButton(); + }, + renderEditBoundaryConditionView: function () { + if(this.editBoundaryConditionView) { + this.editBoundaryConditionView.remove(); + } + this.editBoundaryConditionView = this.renderCollection( + this.collection, + BoundaryConditionView, + this.queryByHook("edit-boundary-conditions-list") + ); + $(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(); + } + 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, + BoundaryConditionView, + this.queryByHook("view-boundary-conditions-list"), + options + ); + }, + resetNewBCViews: function () { + $(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); + $(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.queryByHook("new-bc-deterministic")).prop("disabled", true); + this.toggleAddNewBCButton(); + }, + setDefaultBC: function () { + 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 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) {}, + 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: false, + name: 'name', + valueType: 'string', + 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 + }); + } + }, + newBCValue: { + hook: "new-bc-value", + prepareView: function (el) { + return new InputView({ + parent: this, + required: false, + name: 'value', + tests: tests.valueTests, + valueType: 'number', + }); + } + }, + 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: " + }); + } + }, + 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 diff --git a/client/views/component-types.js b/client/views/component-types.js index d5856b2693..f96375fa28 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 @@ -46,5 +46,6 @@ module.exports = View.extend({ let index = this.parent.model.types.indexOf(typeID); this.parent.model.types.splice(index, 1); } + this.parent.model.trigger('change'); } }); \ No newline at end of file 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 edaff605a7..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 @@ -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 @@ -34,14 +34,65 @@ 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', '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") { @@ -78,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"); @@ -171,9 +186,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 () { @@ -213,6 +228,17 @@ 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{ + let display = this.readOnly ? "none" : "block"; + $(this.queryByHook("external-domains-container")).css("display", display); + } + }, changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); } 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-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-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 20bba307e3..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 @@ -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,14 @@ 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; + }, + 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 +66,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 diff --git a/client/views/edit-event-assignment.js b/client/views/edit-event-assignment.js index 72c4b268d3..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 @@ -24,31 +24,22 @@ 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', + 'change [data-hook=event-assignment-expression]' : 'updateViewer' }, 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({ - label: '', - name: 'variable', - required: true, - idAttributes: 'cid', - options: options, - 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 () { }, @@ -62,44 +53,66 @@ 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 val = Number(e.target.value); 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; } }); } 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', + hook: 'event-assignment-expression', prepareView: function (el) { return new InputView({ parent: this, required: true, name: 'event-assignment-expression', - label: '', - tests: '', + placeholder: "---No Expression Entered---", modelKey: 'expression', valueType: 'string', value: this.model.expression, }); }, }, + 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/edit-function-definition.js b/client/views/edit-function-definition.js index 61a3261b45..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 @@ -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() @@ -57,7 +59,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/edit-initial-condition.js b/client/views/edit-initial-condition.js index 11089e5efc..80b9489814 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 @@ -17,108 +17,78 @@ along with this program. If not, see . */ let $ = require('jquery'); +let _ = require('underscore'); //support files let app = require('../app'); var tests = require('./tests'); +let modals = require('../modals'); //views var View = require('ampersand-view'); 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', '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); 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); - 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, + if(!this.viewMode) { + this.model.on('change', _.bind(this.updateViewer, this)); + } + $(document).on('shown.bs.modal', function (e) { + $('[autofocus]', e.target).focus(); }); - 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), + $(document).on('hide.bs.modal', '.modal', function (e) { + e.target.remove() }); - 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(); + if(!this.model.annotation){ + $(this.queryByHook('edit-annotation-btn')).text('Add') } + this.toggleDetailsView(); }, - 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(); + editAnnotation: function () { + var self = this; + var name = this.model.name; + var annotation = this.model.annotation; + if(document.querySelector('#initialConditionAnnotationModal')) { + document.querySelector('#initialConditionAnnotationModal').remove(); } - this.typesView = this.renderCollection( - this.model.collection.parent.domain.types, - TypesView, - this.queryByHook("initial-condition-types"), - {"filter": function (model) { - return model.typeID != 0; - }} - ); + 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(); + } + }); + okBtn.addEventListener('click', function (e) { + self.model.annotation = input.value.trim(); + self.parent.renderEditInitialConditionsView(); + modal.modal('hide'); + }); }, getSpecieFromSpecies: function (name) { var species = this.model.collection.parent.species.filter(function (specie) { @@ -129,20 +99,62 @@ 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; + 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(); } }, - 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 () {}, + updateViewer: function () { + this.parent.renderViewInitialConditionsView(); }, 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) { @@ -150,13 +162,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/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 9c54a25471..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 @@ -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'); @@ -26,43 +27,26 @@ 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', '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 () { - 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); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)) + } $(document).on('shown.bs.modal', function (e) { $('[autofocus]', e.target).focus(); }); @@ -95,33 +79,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 = Number(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; } }); @@ -131,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', @@ -145,7 +137,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 +172,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 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 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/edit-species.js b/client/views/edit-species.js new file mode 100644 index 0000000000..39a7e6f744 --- /dev/null +++ b/client/views/edit-species.js @@ -0,0 +1,304 @@ +/* +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 . +*/ + +let $ = require('jquery'); +let _ = require('underscore'); +//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 TypesView = require('./component-types'); +let SelectView = require('ampersand-select-view'); +//templates +let editTemplate = require('../templates/includes/editSpecies.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: { + '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.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; + }, + render: function () { + 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(); + }); + 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(); + if(!this.viewMode){ + this.model.on('change', _.bind(this.updateViewer, this)); + if(this.parent.spatial) { + this.renderTypes(); + } + } + }, + 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(); + }, + 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); + 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 () { + 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 + 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) {}, + updateViewer: function () { + this.parent.renderViewSpeciesView() + }, + 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: this.parent.spatial ? 'diffusion constant' : 'value', + tests: tests.valueTests, + modelKey: this.parent.spatial ? 'diffusionConst' : 'value', + valueType: 'number', + value: this.parent.spatial ? this.model.diffusionConst : 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/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 917820a137..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 @@ -16,6 +16,9 @@ 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'); //views var View = require('ampersand-view'); var EditEventAssignment = require('./edit-event-assignment'); @@ -25,25 +28,56 @@ 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); + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.tooltips = attrs.tooltips; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderCollection( + 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(); + } + }, + 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(); + } + this.editEventAssignments = this.renderCollection( this.collection, EditEventAssignment, - this.queryByHook('event-assignments-container') + 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('view-event-assignments-container'), + options ); }, update: function () { }, updateValid: function () { - }, - addAssignment: function () { - this.collection.addEventAssignment(); - this.collection.parent.collection.trigger('change') - }, + } }) \ 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/event-details.js b/client/views/event-details.js index 3d64cdb23d..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 @@ -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) { @@ -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('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 () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -81,6 +77,7 @@ module.exports = View.extend({ } this.eventAssignmentsView = new EventAssignment({ collection: this.model.eventAssignments, + tooltips: this.parent.tooltips }); app.registerRenderSubview(this, this.eventAssignmentsView, 'event-assignments'); }, @@ -111,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, @@ -126,8 +122,6 @@ module.exports = View.extend({ parent: this, required: true, name: 'priority', - label: '', - tests: '', modelKey: 'priority', valueType: 'string', value: this.model.priority, @@ -141,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, diff --git a/client/views/event-listings.js b/client/views/event-listings.js index 2f248816f7..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 @@ -17,52 +17,36 @@ along with this program. If not, see . */ var $ = require('jquery'); +let _ = require('underscore'); //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 +56,15 @@ 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; + }else{ + this.model.on("change", _.bind(this.updateViewer, this)); + } }, 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 +73,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("view-trigger-time")).prop('checked', true); + }else{ + $(this.queryByHook("view-assignment-time")).prop('checked', true); + } } }, update: function () { @@ -110,7 +109,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 +117,20 @@ 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.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 73bfbcca24..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 @@ -37,54 +37,89 @@ 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.opened = attrs.opened - 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.renderEventListingsView(); - 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() - if(this.opened) { - this.openEventsContainer(); + 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.renderViewEventListingView(); }, update: function () { }, updateValid: function () { }, - renderEventListingsView: function () { - if(this.eventListingsView){ - this.eventListingsView.remove(); + renderEditEventListingsView: function () { + if(this.editEventListingsView){ + this.editEventListingsView.remove(); + } + this.editEventListingsView = this.renderCollection( + this.collection, + EventListings, + 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(); } - this.eventListingsView = this.renderCollection( + 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, EventListings, - this.queryByHook('event-listing-container') + this.queryByHook('view-event-listing-container'), + options ); $(document).ready(function () { $('[data-toggle="tooltip"]').tooltip(); @@ -134,11 +169,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); }, 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/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..54a9c1d4f2 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 @@ -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,24 @@ module.exports = View.extend({ }, initialize: function (attrs, options) { View.prototype.initialize.apply(this, arguments); - this.opened = attrs.opened; + this.tooltips = Tooltips.initialConditionsEditor; + this.readOnly = attrs.readOnly ? attrs.readOnly : false; }, render: function () { View.prototype.render.apply(this, arguments); - this.renderCollection( - this.collection, - EditInitialCondition, - this.queryByHook('initial-conditions-collection') - ); - if(this.opened) { - this.openInitialConditionContainer(); + 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(); } - }, - update: function () { - }, - updateValid: function () { + this.renderViewInitialConditionsView(); }, addInitialCondition: function (e) { var initialConditionType = e.target.textContent; @@ -63,19 +66,51 @@ 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') + ); + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + }); + }); + }, + renderViewInitialConditionsView: function () { + 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, + EditInitialCondition, + 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 () {} }); \ No newline at end of file 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 f7d5bebf8a..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 @@ -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,16 +149,16 @@ 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(); $(this.parent.queryByHook('model-run-container')).css("display", "block") var model = this.model var queryStr = "?cmd=start&outfile=none&path="+model.directory - if(species) { - queryStr += "&species=" + species; + if(target) { + queryStr += "&target=" + target; } var endpoint = path.join(app.getApiPath(), 'model/run')+queryStr; var self = this; diff --git a/client/views/model-viewer.js b/client/views/model-viewer.js index 4006bc62be..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 @@ -20,11 +20,11 @@ along with this program. If not, see . let app = require('../app'); //views 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 ParametersEditor = require('./parameters-editor'); +let EventsViewer = require('./events-editor'); +let RulesViewer = require('./rules-editor'); +let SpeciesViewer = require('./species-editor'); +let ReactionsViewer = require('./reactions-editor'); +let ParametersViewer = require('./parameters-editor'); let SBMLComponentsView = require('./sbml-component-editor'); //templates let template = require('../templates/includes/modelViewer.pug'); @@ -48,12 +48,13 @@ 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"); }, renderParametersView: function () { - let parametersViewer = new ParametersEditor({ + let parametersViewer = new ParametersViewer({ collection: this.model.parameters, readOnly: true }); @@ -61,26 +62,31 @@ module.exports = View.extend({ }, renderReactionsView: function () { let reactionsViewer = new ReactionsViewer({ - collection: this.model.reactions + collection: this.model.reactions, + readOnly: true }); app.registerRenderSubview(this, reactionsViewer, "reactions-viewer-container"); }, renderRulesView: function () { let rulesViewer = new RulesViewer({ - collection: this.model.rules + collection: this.model.rules, + readOnly: true }); app.registerRenderSubview(this, rulesViewer, "rules-viewer-container"); }, renderSBMLComponentsView: function () { let sbmlComponentsView = new SBMLComponentsView({ functionDefinitions: this.model.functionDefinitions, - viewMode: true + readOnly: true }); app.registerRenderSubview(this, sbmlComponentsView, "sbml-components-viewer-container"); }, renderSpeciesView: function () { let speciesViewer = new SpeciesViewer({ - collection: this.model.species + collection: this.model.species, + spatial: this.model.is_spatial, + defaultMode: this.model.defaultMode, + readOnly: true }); app.registerRenderSubview(this, speciesViewer, "species-viewer-container"); }, 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 36e1949844..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 @@ -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 864c24a43d..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 @@ -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'); @@ -25,10 +26,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 +59,23 @@ 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) + }); + } + }else{ + this.model.on('change', _.bind(this.updateViewer, this)); + } }, 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,10 +116,13 @@ module.exports = View.extend({ }); okBtn.addEventListener('click', function (e) { self.model.annotation = input.value.trim(); - self.parent.renderReactionListingView(); + self.parent.renderEditReactionListingView(); modal.modal('hide'); }); }, + updateViewer: function () { + this.parent.renderViewReactionView(); + }, subviews: { inputName: { hook: 'input-name-container', 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 c54ef00484..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 @@ -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')) @@ -206,8 +237,9 @@ module.exports = View.extend({ return ReactionTypes[type].label }, toggleProcessError: function () { - let errorMsg = $(this.queryByHook('process-component-error')) 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/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/rules-editor.js b/client/views/rules-editor.js index fdba4cb842..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 @@ -31,38 +31,70 @@ 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) { + 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 - this.opened = args.opened }, render: function () { View.prototype.render.apply(this, arguments); - this.renderRules(); - this.toggleAddRuleButton() - if(this.opened) { - this.openRulesContainer(); + 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 () { }, updateValid: function () { }, - renderRules: function () { + renderEditRules: function () { if(this.rulesView) { this.rulesView.remove(); } this.rulesView = this.renderCollection( this.collection, RuleView, - this.queryByHook('rule-list-container') + this.queryByHook('edit-rule-list-container') + ); + $(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 ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); @@ -70,7 +102,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 @@ -79,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"); @@ -87,15 +119,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); }, 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/sbml-component-editor.js b/client/views/sbml-component-editor.js index 565b097bc4..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 @@ -36,22 +36,58 @@ 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(); + 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(); }, - renderEdirFunctionDefinitionView: function () { + renderEditFunctionDefinitionView: function () { if(this.editFunctionDefinitionView){ this.editFunctionDefinitionView.remove(); } this.editFunctionDefinitionView = this.renderCollection( this.functionDefinitions, EditFunctionDefinition, - this.queryByHook('function-definition-list') + this.queryByHook('edit-function-definition-list') + ); + $(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 ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { $('[data-toggle="tooltip"]').tooltip("hide"); 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 e144b43d22..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 @@ -16,54 +16,53 @@ 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 speciesTemplate = require('../templates/includes/speciesEditor.pug'); +let spatialSpeciesTemplate = require('../templates/includes/spatialSpeciesEditor.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.spatial = attrs.spatial + this.readOnly = attrs.readOnly ? attrs.readOnly : false; + this.template = this.spatial ? spatialSpeciesTemplate : speciesTemplate; + 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,140 +71,105 @@ 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(); + 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{ - 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.toggleSpeciesCollectionError(); + this.renderEditSpeciesView(); } - this.renderEditSpeciesView(); - this.renderSpeciesAdvancedView(); - this.toggleSpeciesCollectionError(); + this.renderViewSpeciesView(); }, - update: function () { - }, - updateValid: function (e) { - }, - getInitialDefaultSpeciesMode: function () { - var self = this; - if(document.querySelector('#defaultModeModal')) { - document.querySelector('#defaultModeModal').remove() + addSpecies: function () { + if(this.parent.model.domain.types) { + var types = this.parent.model.domain.types.map(function (type) { + return type.typeID; + }); + types.shift() + }else{ + var types = [] } - 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"); + this.collection.addSpecie(types); + this.toggleSpeciesCollectionError() + $(function () { + $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').click(function () { + $('[data-toggle="tooltip"]').tooltip("hide"); + + }); }); }, - 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) - }); + changeCollapseButtonText: function (e) { + app.changeCollapseButtonText(this, e); }, - 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'); - } + 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(); } - var editSpecieView = !this.collection.parent.is_spatial ? EditNonspatialSpecieView : EditSpatialSpecieView; + let options = {viewOptions: {parent: this}}; this.editSpeciesView = this.renderCollection( this.collection, - editSpecieView, - this.queryByHook('specie-list') + SpecieView, + this.queryByHook('edit-specie-list') ); - $(document).ready(function () { + $(function () { $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').click(function () { - $('[data-toggle="tooltip"]').tooltip("hide"); - }); + $('[data-toggle="tooltip"]').tooltip("hide"); + + }); }); }, - renderSpeciesAdvancedView: function () { - if(this.collection.parent.is_spatial) { - return - } - if(this.speciesAdvancedView) { - this.speciesAdvancedView.remove() + renderViewSpeciesView: function () { + if(this.viewSpeciesView){ + this.viewSpeciesView.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(); + if(this.defaultMode !== "dynamic") { + $(this.queryByHook("species-switching-header")).css("display", "none"); }else{ - this.addSpecies(); + $(this.queryByHook("species-switching-header")).css("display", "block"); } - }, - addSpecies: function () { - if(this.parent.model.domain.types) { - var types = this.parent.model.domain.types.map(function (type) { - return type.typeID; - }); - types.shift() + this.containsMdlWithAnn = this.collection.filter(function (model) {return model.annotation}).length > 0; + if(!this.containsMdlWithAnn) { + $(this.queryByHook("species-annotation-header")).css("display", "none"); }else{ - var types = [] + $(this.queryByHook("species-annotation-header")).css("display", "block"); } - this.collection.addSpecie(types); - this.toggleSpeciesCollectionError() - $(document).ready(function () { + 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"); @@ -214,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') @@ -222,12 +187,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 +}); 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/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 22b8cd3fbf..c0f41ec245 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 @@ -31,15 +31,31 @@ module.exports = View.extend({ template: template, events: { 'click [data-hook=collapse]' : 'changeCollapseButtonText', - }, - bindings: { + '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' }, initialize: function (attrs, options) { 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); + 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) { }, @@ -49,6 +65,18 @@ module.exports = View.extend({ changeCollapseButtonText: function (e) { app.changeCollapseButtonText(this, e); }, + setTimestepSize: function (e) { + this.model.timestepSize = Number("1e-" + 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 = Number("1e-" + e.target.value); + $(this.queryByHook("timestep-size-value")).html(value); + }, subviews: { inputSimEnd: { hook: 'preview-time', 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 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 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-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 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 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 426635b43e..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 @@ -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/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 b4a4a62755..04c210ad94 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 @@ -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 @@ -406,3 +406,34 @@ 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') + 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") + 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) + except StochSSAPIError as err: + report_error(self, log, err) + self.finish() 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 013f2b3d91..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 @@ -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/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 c2fd231b00..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 @@ -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 diff --git a/stochss/handlers/util/stochss_base.py b/stochss/handlers/util/stochss_base.py index c17075e87f..43a5b73ea8 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 @@ -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]) 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 bb2b9ba9d9..3d0dbaecbe 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 @@ -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'] = 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_notebook.py b/stochss/handlers/util/stochss_notebook.py index baaadd017b..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 @@ -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()}"), @@ -137,7 +165,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"]}", ' @@ -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) @@ -769,13 +798,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'] 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})" + 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: - ts_str += ")" + ts_str = f'{pad}self.timespan(np.arange(0, {end + output_freq}, {output_freq}))' tspan.append(ts_str) model.extend(tspan) @@ -904,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)]) 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 1c930fed1e..60a9191783 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 @@ -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 @@ -169,8 +197,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 " @@ -350,6 +379,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) @@ -358,6 +389,21 @@ def convert_to_spatialpy(self): return s_model + def create_boundary_condition(self, kwargs): + ''' + Create a new boundary condition using spatialpy.BoundaryCondition + + Attributes + ---------- + kwargs : dict + Arguments passed to the spatialpy.BoundaryCondition constructor + ''' + model = self.convert_to_spatialpy() + new_bc = BoundaryCondition(model=model, **kwargs) + expression = new_bc.expression() + return {"expression": expression} + + def get_domain(self, path=None, new=False): ''' Get a prospective domain @@ -525,10 +571,14 @@ 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'] = 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(): 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']))) 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/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)) 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..896b1a3be4 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 @@ -34,31 +34,21 @@ args = parser.parse_args() print(os.path.dirname(__file__)) - # 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_model_template + import test_settings_template + import test_stochss_base + import test_gillespy2 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_model_template, + test_settings_template, + test_gillespy2, + 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_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_gillespy2.py b/stochss/tests/test_gillespy2.py new file mode 100644 index 0000000000..b16fbfa94a --- /dev/null +++ b/stochss/tests/test_gillespy2.py @@ -0,0 +1,167 @@ +''' +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 example_models import ( + Brusselator, + Degradation, + Dimerization, + LotkavolterraOscillator, + MichaelisMenten, + Opioid, + Schlogl, + ToggleSwitch, + VilarOscillator, + Oregonator, + TysonOscillator +) + +os.chdir('/stochss') + +# 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.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 + + 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 = 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 = 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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.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) 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_model_template.py b/stochss/tests/test_model_template.py index d40e639bd5..3e6812f079 100644 --- a/stochss/tests/test_model_template.py +++ b/stochss/tests/test_model_template.py @@ -1,19 +1,48 @@ +''' +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 unittest import json import os os.chdir('/stochss') -template_path = "stochss_templates/nonSpatialModelTemplate.json" +class TestModelTemplate(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for model template completeness. + ################################################################################################ + ''' + 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: + self.template = json.load(template_file) -class TestNonSpatialModelTemplate(unittest.TestCase): def test_model_elements(self): - with open(template_path, "r") as template_file: - template = json.load(template_file) - - template_keys = sorted(list(template.keys())) + ''' + 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" with open(model_path, "r") as model_file: @@ -24,21 +53,40 @@ 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) + 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 model. + ''' + template_keys = sorted(list(self.template['modelSettings'].keys())) + mdl_settings_path = "client/models/timespan-settings.js" - template_keys = sorted(list(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) + + def test_model_domain_elements(self): + ''' + Check if the domain in the model template has all of the + properties currently in the domain model. + ''' + 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) 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_settings_template.py b/stochss/tests/test_settings_template.py index a53f417704..8db314b230 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-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 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) diff --git a/stochss/tests/test_stochss_base.py b/stochss/tests/test_stochss_base.py new file mode 100644 index 0000000000..11e2f1f920 --- /dev/null +++ b/stochss/tests/test_stochss_base.py @@ -0,0 +1,857 @@ +''' +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 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') + +# pylint: disable=too-many-public-methods +# pylint: disable=line-too-long +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.user_dir = os.path.expanduser("~") + self.tempdir = tempfile.TemporaryDirectory() + test_filename = "test_base_file" + 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) + os.mkdir(self.test_folderpath) + + + def tearDown(self): + ''' Cleanup the temp directory after each test. ''' + self.tempdir.cleanup() + if StochSSBase.user_dir != os.path.expanduser("~"): + StochSSBase.user_dir = os.path.expanduser("~") + + ################################################################################################ + # 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)) + + ################################################################################################ + # 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.user_dir = self.test_folderpath + StochSSBase.get_new_path(dst_path=test_dst_path) + self.assertTrue(os.path.exists(os.path.join(self.test_folderpath, "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") 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" diff --git a/stochss_templates/nonSpatialModelTemplate.json b/stochss_templates/nonSpatialModelTemplate.json index 248896e2cc..b48bc4cbd9 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, @@ -40,5 +41,6 @@ "reactions": [], "rules": [], "eventsCollection": [], - "functionDefinitions": [] + "functionDefinitions": [], + "boundaryConditions": [] } 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