You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2021/06/18 19:38:35 UTC
[airavata-django-portal] 18/20: AIRAVATA-3453 Database model for
registering custom application templates
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch airavata-3453
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit c26ec9127beaaf811156523eecf49cbd5cbdd6ce
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu May 20 13:15:39 2021 -0400
AIRAVATA-3453 Database model for registering custom application templates
---
django_airavata/app_config.py | 3 +-
django_airavata/apps/api/admin.py | 24 +
...template_applicationtemplatecontextprocessor.py | 39 ++
django_airavata/apps/api/models.py | 26 ++
.../jquery.textcomplete.css | 33 --
.../jquery.textcomplete.min.js | 3 -
.../django_airavata_workspace/supcrtbl2.html | 487 ---------------------
django_airavata/apps/workspace/views.py | 58 ++-
8 files changed, 131 insertions(+), 542 deletions(-)
diff --git a/django_airavata/app_config.py b/django_airavata/app_config.py
index f747b15..f34195d 100644
--- a/django_airavata/app_config.py
+++ b/django_airavata/app_config.py
@@ -76,8 +76,7 @@ def get_default_url_home(app_config):
first_named_url = urlpattern.name
break
if not first_named_url:
- raise Exception("{} has no named urls, "
- "can't figure out default home URL")
+ raise Exception(f"{urls} has no named urls, can't figure out default home URL")
if app_name:
return app_name + ":" + first_named_url
else:
diff --git a/django_airavata/apps/api/admin.py b/django_airavata/apps/api/admin.py
index b97a94f..a5d0364 100644
--- a/django_airavata/apps/api/admin.py
+++ b/django_airavata/apps/api/admin.py
@@ -1,2 +1,26 @@
# Register your models here.
+
+from django.contrib import admin
+
+from .models import ApplicationTemplate, ApplicationTemplateContextProcessor
+
+
+class ApplicationTemplateContextProcessorInline(admin.StackedInline):
+ model = ApplicationTemplateContextProcessor
+ extra = 1
+
+
+class ApplicationTemplateAdmin(admin.ModelAdmin):
+ fields = ['application_module_id', 'template_path']
+ list_display = ['application_module_id', 'template_path', 'updated_by', 'updated']
+ inlines = [ApplicationTemplateContextProcessorInline]
+
+ def save_model(self, request, obj, form, change):
+ obj.updated_by = request.user
+ if not obj.pk:
+ obj.created_by = request.user
+ return super().save_model(request, obj, form, change)
+
+
+admin.site.register(ApplicationTemplate, ApplicationTemplateAdmin)
diff --git a/django_airavata/apps/api/migrations/0006_applicationtemplate_applicationtemplatecontextprocessor.py b/django_airavata/apps/api/migrations/0006_applicationtemplate_applicationtemplatecontextprocessor.py
new file mode 100644
index 0000000..688cc63
--- /dev/null
+++ b/django_airavata/apps/api/migrations/0006_applicationtemplate_applicationtemplatecontextprocessor.py
@@ -0,0 +1,39 @@
+# Generated by Django 2.2.17 on 2021-05-20 17:13
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('django_airavata_api', '0005_delete_user_files'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ApplicationTemplate',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('application_module_id', models.CharField(max_length=255, unique=True)),
+ ('template_path', models.TextField()),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('updated', models.DateTimeField(auto_now=True)),
+ ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
+ ('updated_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='ApplicationTemplateContextProcessor',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('callable_path', models.CharField(max_length=255)),
+ ('application_template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='context_processors', to='django_airavata_api.ApplicationTemplate')),
+ ],
+ options={
+ 'unique_together': {('application_template', 'callable_path')},
+ },
+ ),
+ ]
diff --git a/django_airavata/apps/api/models.py b/django_airavata/apps/api/models.py
index 3826d9e..b6b7e27 100644
--- a/django_airavata/apps/api/models.py
+++ b/django_airavata/apps/api/models.py
@@ -1,3 +1,4 @@
+from django.conf import settings
from django.db import models
@@ -33,3 +34,28 @@ class User_Notifications(models.Model):
username = models.CharField(max_length=64)
notification_id = models.CharField(max_length=255)
is_read = models.BooleanField(default=False)
+
+
+class ApplicationTemplate(models.Model):
+ application_module_id = models.CharField(max_length=255, unique=True)
+ template_path = models.TextField()
+ created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="+")
+ updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="+")
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ def __str__(self):
+ return f"{self.application_module_id}: {self.template_path}"
+
+
+class ApplicationTemplateContextProcessor(models.Model):
+ application_template = models.ForeignKey(ApplicationTemplate, on_delete=models.CASCADE, related_name="context_processors")
+ # Use django.util.module_loading.import_string to import
+ # https://docs.djangoproject.com/en/3.2/ref/utils/#module-django.utils.module_loading
+ callable_path = models.CharField(max_length=255)
+
+ class Meta:
+ unique_together = (('application_template', 'callable_path'),)
+
+ def __str__(self):
+ return self.callable_path
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/jquery.textcomplete.css b/django_airavata/apps/workspace/static/django_airavata_workspace/jquery.textcomplete.css
deleted file mode 100644
index 37a761b..0000000
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/jquery.textcomplete.css
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Sample */
-
-.dropdown-menu {
- border: 1px solid #ddd;
- background-color: white;
-}
-
-.dropdown-menu li {
- border-top: 1px solid #ddd;
- padding: 2px 5px;
-}
-
-.dropdown-menu li:first-child {
- border-top: none;
-}
-
-.dropdown-menu li:hover,
-.dropdown-menu .active {
- background-color: rgb(110, 183, 219);
-}
-
-
-/* SHOULD not modify */
-
-.dropdown-menu {
- list-style: none;
- padding: 0;
- margin: 0;
-}
-
-.dropdown-menu a:hover {
- cursor: pointer;
-}
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/jquery.textcomplete.min.js b/django_airavata/apps/workspace/static/django_airavata_workspace/jquery.textcomplete.min.js
deleted file mode 100644
index ce1536d..0000000
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/jquery.textcomplete.min.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! jquery-textcomplete - v1.8.4 - 2017-08-29 */
-!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"==typeof module&&module.exports){var b=require("jquery");module.exports=a(b)}else a(jQuery)}(function(a){if("undefined"==typeof a)throw new Error("jQuery.textcomplete requires jQuery");return+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)},c=1;a.fn.textcomplete=function(d,e){var f=Array.prototype.slice.call(arguments);return this.each(function(){var g=this,h=a(this),i=h.da [...]
-//# sourceMappingURL=dist/jquery.textcomplete.min.map
\ No newline at end of file
diff --git a/django_airavata/apps/workspace/templates/django_airavata_workspace/supcrtbl2.html b/django_airavata/apps/workspace/templates/django_airavata_workspace/supcrtbl2.html
deleted file mode 100644
index 945026f..0000000
--- a/django_airavata/apps/workspace/templates/django_airavata_workspace/supcrtbl2.html
+++ /dev/null
@@ -1,487 +0,0 @@
-{% extends 'base.html' %}
-
-{% load static %}
-{% load render_bundle from webpack_loader %}
-
-{% block css %}
-{% comment %} TODO: are chunk-vendors and chunk-common needed? {% endcomment %}
-{% comment %} {% render_bundle 'chunk-vendors' 'css' 'WORKSPACE' %} {% endcomment %}
-{% comment %} {% render_bundle 'chunk-common' 'css' 'WORKSPACE' %} {% endcomment %}
-{% comment %} {% render_bundle 'adpf' 'css' 'WORKSPACE' %} {% endcomment %}
- <link href="{% static 'django_airavata_workspace/jquery.textcomplete.css' %}" rel="stylesheet" type="text/css">
-{% endblock %}
-
-{% block content %}
-{% comment %} TODO: maybe use wc- as a prefix? {% endcomment %}
-<div class="main-content-wrapper">
- <main class="main-content">
- <div class="container-fluid">
- <h1>SUPCRTBL Online Version 1.0.1</h2>
- <adpf-experiment-editor
- id="experiment-editor"
- application-id="supcrtbl_eb4216b3-fcbf-4422-a70d-27af2550cfb6"
- {% if experiment_id %}
- experiment-id="{{ experiment_id }}"
- {% endif %} >
- <div id="solvent" slot="Specify Solvent Phase Region">
- <label for="solventPhase">Specify solvent phase region:</label>
- <input type="radio" name="solventPhase" value="0">One-phase region</input> <br/>
- <input type="radio" name="solventPhase" value="1">liquid vapor saturation curve</input>
- </div>
- <div id="lipVapSat" slot="Specify Independent Liq-vap Saturation Variable (3)">
- <label for="lipVapSatVar">Specify independent liq-vap saturation variable:</label>
- <input type="radio" name="lipVapSatVar" value="0">Temperature (degCel)</input> <br/>
- <input type="radio" name="lipVapSatVar" value="1">Pressure (bars)</input>
- </div>
- <div id="indVar" slot="Specify Independent State Variables">
- <label for="independentStateVar">Specify independent State Variables:</label>
- <input type="radio" name="independentStateVar" value="0" >Temperature (degCel), density(H2O) (g/cc)</input> <br/>
- <input type="radio" name="independentStateVar" value="1">Temperature (degCel), pressure (bars)</input>
- </div>
- <div id="univariantCurve" slot="Univariant Curve Option (2)">
- <label for="univariantCurveOption">Would you like to use the univariant curve option?
- (i.e., calculate T(logK,P) or P(logK,T):</label>
- <input type="radio" name="univariantCurveOption" value="0">Yes</input> <br/>
- <input type="radio" name="univariantCurveOption" value="1">No</input>
- </div>
- <div id="tabulationBaric" slot="Specify Tabulation Option (2)">
- <label for="tabulationBaricOption">Specify tabulation option:</label>
- <input type="radio" name="tabulationBaricOption" value="0">Calculate ISOBARIC (T) tables</input> <br/>
- <input type="radio" name="tabulationBaricOption" value="1">Calculate ISOTHERMAL (D) tables</input>
- </div>
- <div id="tabulationChoric" slot="Specify Tabulation Option (1)">
- <label for="tabulatioChoricnOption">Specify tabulation option:</label>
- <input type="radio" name="tabulationChoricOption" value="0">Calculate ISOCHORIC (T) tables</input> <br/>
- <input type="radio" name="tabulationChoricOption" value="1">Calculate ISOTHERMAL (D) tables</input>
- </div>
- <!-- TODO: Specify Table-increment Option (2) ? -->
- <!-- TODO: Specify Table-increment Option (3) ?-->
- <div id="table" slot="Specify Table Increment Option (1)">
- <label for="tableIncrement">Specify table-increment option:</label>
- <input type="radio" name="tableIncrement" value="0">Calculate tables having uniform increments</input> <br/>
- <input type="radio" name="tableIncrement" value="1">Calculate tables having unequal increments</input>
- </div>
- <div id="univariantCalc" slot="Specify Univariant Calculation Option (2)">
- <label for="univariantCalcOption">Specify univariant calculation option:</label>
- <input type="radio" name="univariantCalcOption" value="0">Calculate T (logK, isobars)</input> <br/>
- <input type="radio" name="univariantCalcOption" value="1">Calculate P (logK, isotherms)</input>
- </div>
- <div id="isochores" slot="Specify ISOCHORES (g/cc) Range (1)">
- <label for="isochoresRange">Specify ISOCHORES(g/cc) range: <br/>
- min, max, increment</label>
- <input type="text" name="isochoresRange" id="isochoresRange" />
- </div>
- <!-- TODO: Specify TEMP(degCel) Range (2) ? -->
- <!-- TODO: Specify TEMP(degCel) Range (3) ? -->
- <div id="temp" slot="Specify TEMP(degCel) Range (1)">
- <label for="tempRange">Specify TEMP (degCel) range: <br/>
- min, max, increment</label>
- <input type="text" name="tempRange" id="tempRange" />
- </div>
- <div id="dH2OTemp" slot="Specify DH2O(g/cc) TEMP(degCel) ValuePairs (1)">
- <label for="dH2OTempPairs">Specify DH2O(g/cc), TEMP (degCel) value pairs: <br/>
- One pair per line, ending with 0,0</label>
- <textarea name="dH2OTempPairs" id="dH2OTempPairs" ></textarea>
- </div>
- <!-- TODO: or "Specify ISOTHERMS(degCel) Range (2)" ?-->
- <div id="isotherms" slot="Specify ISOTHERMS(degCel) range (1)">
- <label for="isothermsRange">Specify ISOTHERMS (degCel) range: <br/>
- min, max, increment</label>
- <input type="text" name="isothermsRange" id="isothermsRange" />
- </div>
- <div id="dH2O" slot="Specify DH2O(g/cc) Range (1)">
- <label for="dH2ORange">Specify DH2O (g/cc) range: <br/>
- min, max, increment</label>
- <input type="text" name="dH2ORange" id="dH2ORange" />
- </div>
- <div id="tempDH2O" slot="Specify TEMP(degCel) DH2O(g/cc) Value Pairs (1)">
- <label for="tempDH2OPairs">Specify TEMP (degCel), DH2O(g/cc) value pairs: <br/>
- One pair per line, ending with 0,0</label>
- <textarea name="tempDH2OPairs" id="tempDH2OPairs" ></textarea>
- </div>
- <div id="isobars" slot="Specify ISOBARS(bars) Range (2)">
- <label for="isobarsRange">Specify ISOBARS(bars) range: <br/>
- min, max, increment</label>
- <input type="text" name="isobarsRange" id="isobarsRange" />
- </div>
- <div id="logK" slot="Specify LogK Range (2)">
- <label for="logKRange">Specify logK range: <br/>
- Kmin, Kmax, Kincrement</label>
- <input type="text" name="logKRange" id="logKRange" />
- </div>
- <div id="logKBoundingTemp" slot="Specify Bounding TEMP(degCel) Range (2)">
- <label for="logKBoundingTempRange">Specify bounding TEMP (degCel) range: <br/>
- T min, T max:</label>
- <input type="text" name="logKBoundingTempRange" id="logKBoundingTempRange" />
- </div>
- <div id="logKBoundingPres" slot="Specify Bounding PRES(bars) Range (2)">
- <label for="logKBoundingPresRange">Specify bounding PRES (bars) range: <br/>
- P min, P max:</label>
- <input type="text" name="logKBoundingPresRange" id="logKBoundingPresRange" />
- </div>
- <!-- TODO: what input does this match? -->
- <div id="presTemp">
- <label for="presTempPairs">Specify PRES (bars), TEMP (degCel) value pairs: <br/>
- One pair per line, ending with 0,0</label>
- <textarea name="presTempPairs" id="presTempPairs"></textarea>
- </div>
- <!-- TODO: or Specify PRES(bars) range (2) ?-->
- <div id="pres" slot="Specify PRES(bars) Range (3)">
- <label for="presRange">Specify PRES (bars) range: <br/>
- min, max, increment</label>
- <input type="text" name="presRange" id="presRange" />
- </div>
- <div id="tempPres" slot="Specify TEMP(degCel) Pres(g/cc) Value Pairs (2)">
- <label for="tempPresPairs">Specify TEMP (degCel), Pres(g/cc) value pairs: <br/>
- One pair per line, ending with 0,0</label>
- <textarea name="tempPresPairs" id="tempPresPairs" ></textarea>
- </div>
- <div id="lipVapSatTemp" slot="Specify Liq-vap Saturation TEMP(degCel) Values (3)">
- <label for="lipVapSatTempVal">Specify liq-vap saturation TEMP (degCel) values: <br/>
- One per line, concluding with 0</label>
- <textarea name="lipVapSatTempVal" id="lipVapSatTempVal" ></textarea>
- </div>
- <div id="lipVapSatPres" slot="Specify Liq-vap Saturation PRES(bars) Values (3)">
- <label for="lipVapSatPresVal">Specify liq-vap saturation PRES (bars) values: <br/>
- One per line, concluding with 0</label>
- <textarea name="lipVapSatPresVal" id="lipVapSatPresVal" ></textarea>
- </div>
- <br/><br/>
- <div id="lipVapSatPres" slot="Reaction">
- <label for="reaction">Insert reactions here, 1 species per line, empty line between reactions <br/>
- Numbers are the stoichiometric coefficient of the species. <br/>
- Positive numbers are products and negative numbers are reactants, e.g. <br/>
- QUARTZ => SiO2,aq: <br/>
- <code>
- -1 QUARTZ <br/>
- 1 SiO2,aq
- </code>
- </label>
- <textarea rows="15" name="reaction" id="reaction" required style="width:200px;"></textarea>
- </div>
- <br/><br/>
- <!-- TODO: what input does this match? -->
- <div id="kalFormat">
- <label for="kalFormatOption">Specify option for x-y plot files:</label>
- <input type="radio" name="kalFormatOption" value="0" required>Do not generate plot files</input> <br/>
- <input type="radio" name="kalFormatOption" value="1">Generate plot files in generic format</input>
- </div>
- <br/><br/>
- </adpf-experiment-editor>
- </div>
- </main>
-</div>
-{% endblock content %}
-
-{% block scripts %}
-{% comment %} {% render_bundle 'chunk-vendors' 'js' 'WORKSPACE' %} {% endcomment %}
-<script src="{% static 'django_airavata_workspace/wc/adpf.chunk-vendors.js' %}"></script>
-{% comment %} {% render_bundle 'chunk-common' 'js' 'WORKSPACE' %} {% endcomment %}
-{% comment %} {% render_bundle 'adpf' 'js' 'WORKSPACE' %} {% endcomment %}
-<script src="{% static 'django_airavata_workspace/wc/adpf.min.js' %}"></script>
-<script src="{% static 'django_airavata_workspace/jquery.textcomplete.min.js' %}"></script>
-<script>
-document.getElementById("experiment-editor").addEventListener('loaded', e => {
- const [experiment] = e.detail;
- for (const input of experiment.experimentInputs) {
- const slotEl = document.querySelector(`[slot="${input.name}"]`);
- // console.log("slotEl=", slotEl);
- if (slotEl) {
- const inputEls = slotEl.querySelectorAll('input');
- const textareaEl = slotEl.querySelector('textarea');
- // console.log("inputEls=", inputEls);
- if (inputEls && inputEls.length > 0) {
- if (inputEls[0].type === 'text') {
- inputEls[0].value = input.value;
- } else if (inputEls[0].type === 'radio') {
- for (radio of inputEls) {
- if (radio.value === input.value) {
- radio.checked = true;
- break;
- }
- }
- }
- } else if (textareaEl) {
- textareaEl.value = input.value;
- }
- }
- }
- resetViews();
-});
-
-$('document').ready(function() {
- $('#reaction').textcomplete([{
- match: /(^|\b)(\S{1,})$/,
- search: function (term, callback) {
- var words = [
- {% for a_species in species %}"{{ a_species|escapejs }}", {% endfor %}
- ];
- callback($.map(words, function (word) {
- return word.toLowerCase().indexOf(term.toLowerCase()) === 0 ? word : null;
- }));
- },replace: function (word) {
- return word + ' ';
- }
- }]).on('textComplete:select', e => {
- // Keep the value of 'Reaction' up-to-date when an autocomplete option is selected.
- // experiment-editor listens for 'input' events, which are triggered when user
- // types in 'Reaction' textarea, but selecting an autocomplete option doesn't
- // trigger an 'input' event, so we need to manually update the value of the
- // experiment input when an autocomplete option is selected.
- document.getElementById('experiment-editor').vueComponent.updateInputValue('Reaction', e.target.value);
- });
-});
-
-function resetViews() {
- $('#lipVapSat').hide();
- $('#indVar').hide();
- $('#tabulationBaric').hide();
- $('#tabulationChoric').hide();
- $('#table').hide();
- $('#univariantCurve').hide();
- $('#univariantCalc').hide();
- $('#isochores').hide();
- $('#temp').hide();
- $('#dH2OTemp').hide();
- $('#logK').hide();
- $('#isotherms').hide();
- $('#dH2O').hide();
- $('#tempDH2O').hide();
- $('#isobars').hide();
- $('#logKBoundingTemp').hide();
- $('#logKBoundingPres').hide();
- $('#presTemp').hide();
- $('#pres').hide();
- $('#tempPres').hide();
- $('#lipVapSatTemp').hide();
- $('#lipVapSatPres').hide();
- $('#submit').hide();
- if($('input:radio[name=solventPhase]:checked').val() == "0"){
- $('#indVar').show();
- if($('input:radio[name=independentStateVar]:checked').val() == "0"){
- $('#tabulationChoric').show();
- if($('input:radio[name=tabulationChoricOption]:checked').val() == "0"){
- $('#table').show();
- if($('input:radio[name=tableIncrement]:checked').val() == "0"){
- $('#isochores').show();
- $('#temp').show();
- $('#submit').show();
-
- }
- else if($('input:radio[name=tableIncrement]:checked').val() == "1"){
- $('#dH2OTemp').show();
- $('#submit').show();
- }
- }
- else if($('input:radio[name=tabulationChoricOption]:checked').val() == "1"){
- $('#table').show();
- if($('input:radio[name=tableIncrement]:checked').val() == "0"){
- $('#isotherms').show();
- $('#dH2O').show();
- $('#submit').show();
-
- }
- else if($('input:radio[name=tableIncrement]:checked').val() == "1"){
- $('#tempDH2O').show();
- $('#submit').show();
- }
- }
- }
- else if($('input:radio[name=independentStateVar]:checked').val() == "1"){
- $('#univariantCurve').show();
- if($('input:radio[name=univariantCurveOption]:checked').val() == "0"){
- $('#univariantCalc').show();
- if($('input:radio[name=univariantCalcOption]:checked').val() == "0") {
- $('#isobars').show();
- $('#logK').show();
- $('#logKBoundingTemp').show();
- $('#submit').show();
- }
- else if($('input:radio[name=univariantCalcOption]:checked').val() == "1"){
- $('#isotherms').show();
- $('#logK').show();
- $('#logKBoundingPres').show();
- $('#submit').show();
- }
- }
- if($('input:radio[name=univariantCurveOption]:checked').val() == "1"){
- $('#tabulationBaric').show();
- if($('input:radio[name=tabulationBaricOption]:checked').val() == "0"){
- $('#table').show();
- if($('input:radio[name=tableIncrement]:checked').val() == "0"){
- $('#isobars').show();
- $('#temp').show();
- $('#submit').show();
-
- }
- else if($('input:radio[name=tableIncrement]:checked').val() == "1"){
- $('#presTemp').show();
- $('#submit').show();
- }
- }
- else if($('input:radio[name=tabulationBaricOption]:checked').val() == "1"){
- $('#table').show();
- if($('input:radio[name=tableIncrement]:checked').val() == "0"){
- $('#isotherms').show();
- $('#pres').show();
- $('#submit').show();
- }
- else if($('input:radio[name=tableIncrement]:checked').val() == "1"){
- $('#tempPres').show();
- $('#submit').show();
- }
- }
- }
-
- }
- }
- else if($('input:radio[name=solventPhase]:checked').val() == "1"){
- $('#lipVapSat').show();
- if($('input:radio[name=lipVapSatVar]:checked').val() == "0"){
- $('#table').show();
- if($('input:radio[name=tableIncrement]:checked').val() == "0"){
- $('#temp').show();
- $('#submit').show();
- }
- if($('input:radio[name=tableIncrement]:checked').val() == "1"){
- $('#lipVapSatTemp').show();
- $('#submit').show();
- }
- }
- if($('input:radio[name=lipVapSatVar]:checked').val() == "1"){
- $('#table').show();
- if($('input:radio[name=tableIncrement]:checked').val() == "0"){
- $('#pres').show();
- $('#submit').show();
- }
- if($('input:radio[name=tableIncrement]:checked').val() == "1"){
- $('#lipVapSatPres').show();
- $('#submit').show();
- }
- }
- }
- }
- $(document).ready(function() {
- resetViews();
- $("input:radio").change(function () {
- resetViews();
- });
- });
-
- function validateExperiment(event) {
- // const [experiment] = event.detail;
- if (!checkOutput()) {
- console.log('save: preventing default');
- event.preventDefault();
- }
- }
- document.getElementById('experiment-editor').addEventListener('save', validateExperiment);
-
- function checkOutput(){
- var filledIn = true;
- var textArr = ["isochoresRange","tempRange","isothermsRange","dH2ORange","isobarsRange","logKRange","presRange","logKBoundingTempRange","logKBoundingPresRange"];
- for(let s of textArr){
- if($("input#"+s).is(":visible")){
- var iv = document.getElementById(s).value;
- var ivs = iv.trim().split(" ");
- var cvs = iv.trim().split(",");
- var ccvs = iv.trim().split(", ");
- var spaceSeperated = false;
- var commaSeperated = false;
- var commaSpaceSeperated = false;
- if(s==="logKBoundingPresRange" || s==="logKBoundingTempRange"){
- if(ccvs.length==2){
- commaSpaceSeperated=true;
- if(parseInt(ccvs[0],10)>parseInt(ccvs[1],10) || parseInt(ccvs[0],10) < 0 || parseInt(ccvs[1],10) < 0){alert("invalid values for "+s); return false;}
- }
- if(ivs.length==2 && !commaSpaceSeperated){
- spaceSeperated=true;
- if(parseInt(ivs[0],10)>parseInt(ivs[1],10) || parseInt(ivs[0],10) < 0 || parseInt(ivs[1],10) < 0){alert("invalid values for "+s); return false;}
- }
- if(cvs.length==2 && !commaSpaceSeperated){
- commaSeperated=true;
- if(parseInt(cvs[0],10)>parseInt(cvs[1],10) || parseInt(cvs[0],10) < 0 || parseInt(cvs[1],10) < 0){alert("invalid values for "+s); return false;}
- }
- }
- else{
- if(ccvs.length==3){
- commaSpaceSeperated=true;
- if(parseInt(ccvs[0],10)>parseInt(ccvs[1],10) || parseInt(ccvs[0],10) < 0 || parseInt(ccvs[1],10) < 0){alert("invalid values for "+s); return false;}
- }
- if(ivs.length==3 && !commaSpaceSeperated){
- spaceSeperated=true;
- if(parseInt(ivs[0],10)>parseInt(ivs[1],10) || parseInt(ivs[0],10) < 0 || parseInt(ivs[1],10) < 0){alert("invalid values for "+s); return false;}
- }
- if(cvs.length==3 && !commaSpaceSeperated){
- commaSeperated=true;
- if(parseInt(cvs[0],10)>parseInt(cvs[1],10) || parseInt(cvs[0],10) < 0 || parseInt(cvs[1],10) < 0){alert("invalid values for "+s); return false;}
- }
- if(parseInt(ivs[0],10)>parseInt(ivs[1],10) || parseInt(ivs[0],10) < 0 || parseInt(ivs[1],10) < 0 || parseInt(ivs[2],10) < 0){alert("invalid values for "+s); return false;}
- }
- if(spaceSeperated){
- for(let i of ivs){if(!/^-?[0-9.]+\s*$/.test(i)){alert("single space seperated: values must be ints or floats for "+s); return false;}}
- }
- if(commaSeperated){
- for(let i of cvs){if(!/^-?[0-9.]+\s*$/.test(i)){alert("comma seperated: values must be ints or floats for "+s); return false;}}
- }
- if(commaSpaceSeperated){
- for(let i of ccvs){if(!/^-?[0-9.]+\s*$/.test(i)){alert("comma & space seperated: values must be ints or floats for "+s); return false;}}
- }
- if(!commaSpaceSeperated && !spaceSeperated && !commaSeperated){alert("space or comma seperated values per line for "+s); return false;}
- }
- }
- var textAreaArr = ["dH2OTempPairs","tempDH2OPairs","presTempPairs","tempPresPairs","lipVapSatTempVal","lipVapSatPresVal"];
- for(let s of textAreaArr){
- if($("textarea#"+s).is(":visible")){
- var iv = document.getElementById(s).value;
- var ivl = iv.trim().split(/\r|\n/);
- for(let ivlv of ivl){
- var ccvs = ivlv.trim().split(", ");
- var ivs = ivlv.trim().split(",");
- var commaSpaceSeperated = ccvs.length>1;
- var commaSeperated = !commaSpaceSeperated && ivs.length>1;
- if(commaSeperated){
- for(let i of ivs){
- if(!/^-?[0-9.]+\s*$/.test(i)){alert("values must be ints or floats for "+s); return false;}
- if(parseInt(i,10) < 0){alert("invalid values for "+s); return false;}
- }
- }
- if(commaSpaceSeperated){
- for(let i of ccvs){
- if(!/^-?[0-9.]+\s*$/.test(i)){alert("values must be ints or floats for "+s); return false;}
- if(parseInt(i,10) < 0){alert("invalid values for "+s); return false;}
- }
- }
- if(s==="lipVapSatPresVal" || s==="lipVapSatTempVal"){
- if(ivs.length!=1){alert("1 value per line for "+s); return false;}
- if(!/^-?[0-9.]+\s*$/.test(ivs)){alert("values must be ints or floats for "+s); return false;}
- }
- else{if(ivs.length!=2){alert("2 comma seperated values per line for "+s); return false;}}
- }
- if(s==="lipVapSatPresVal" || s==="lipVapSatTempVal"){
- if(ivl[ivl.length-1]!=0){alert("must end with 0");return false;}
- }
- else{
- alert("'"+ivl[ivl.length-1].toString().trim()+"'");
- if(ivl[ivl.length-1].toString().trim()!="0,0"){alert("must end with 0,0");return false;}
- }
- }
- }
- var reactiv = document.getElementById("reaction").value.split(/\r|\n/);
- for(let s of reactiv){
- var ivs = s.trim().split(" ");
- // FIXME: no preg_match in JavaScript
- // if(preg_match("/[a-z]/i", $s) && ivs.length!=2){alert("2 space seperated values per line for reaction"); return false;}
- if(!/^-?[0-9]+\s*$/.test(ivs[0])){alert("stoichiometric coefficient must be an int for "+s); return false;}
- }
- $("input:visible:text").each(function(){if($(this).val().length === 0){filledIn = false;}});
- $("textarea:visible").each(function(){if($(this).val().length === 0){filledIn = false;}});
- if(!filledIn){
- alert("Please Fill in All Items");
- return false;
- }
- // if(validOutput){return true;}
- return true;
- }
-</script>
-{% endblock %}
diff --git a/django_airavata/apps/workspace/views.py b/django_airavata/apps/workspace/views.py
index 0b69ede..bd3ca29 100644
--- a/django_airavata/apps/workspace/views.py
+++ b/django_airavata/apps/workspace/views.py
@@ -7,8 +7,10 @@ from airavata.model.application.io.ttypes import DataType
from airavata_django_portal_sdk import user_storage as user_storage_sdk
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
+from django.utils.module_loading import import_string
from rest_framework.renderers import JSONRenderer
+from django_airavata.apps.api import models
from django_airavata.apps.api.views import (
ApplicationModuleViewSet,
ExperimentSearchViewSet,
@@ -69,11 +71,6 @@ def edit_project(request, project_id):
})
-def species_list(request):
- return {
- 'species': ["ALMANDINE","ANDRADITE","GROSSULAR","KNORRINGITE","MAJORITE","PYROPE","SPESSARTINE","CLINOHUMITE","FAYALITE","FORSTERITE","MONTICELLITE","TEPHROITE","ANDALUSITE","KYANITE","Al-MULLITE","Si-MULLITE","Fe-CHLORITOID","Mg-CHLORITOID","Mn-CHLORITOID","Fe-STAUROLITE","Mg-STAUROLITE","Mn-STAUROLITE","HYDROXY-TOPAZ","AKERMANITE","JULGOLDITE(FeFe)","MERWINITE","PUMPELLYITE(FeAl)","PUMPELLYITE(MgAl)","RANKINITE","SPURRITE","TILLEYITE","ZIRCON","CLINOZOISITE","EPIDOTE(ORDERED)", [...]
- }
-
@login_required
def create_experiment(request, app_module_id):
request.active_nav_item = 'dashboard'
@@ -118,24 +115,51 @@ def create_experiment(request, app_module_id):
if 'experiment-data-dir' in request.GET:
context['experiment_data_dir'] = request.GET['experiment-data-dir']
- # Run through context processors
- for processor in [species_list]:
- context.update(species_list(request))
- return render(request,
- # 'django_airavata_workspace/create_experiment.html',
- 'django_airavata_workspace/supcrtbl2.html',
- context)
+ template_path = 'django_airavata_workspace/create_experiment.html'
+ # Apply a custom application template if it exists
+ custom_template_path, custom_context = get_custom_template(request, app_module_id)
+ if custom_template_path is not None:
+ logger.debug(f"Applying custom application template {custom_template_path}")
+ template_path = custom_template_path
+ context.update(custom_context)
+
+ return render(request, template_path, context)
@login_required
def edit_experiment(request, experiment_id):
request.active_nav_item = 'experiments'
- return render(request,
- # 'django_airavata_workspace/edit_experiment.html',
- 'django_airavata_workspace/supcrtbl2.html',
- {'bundle_name': 'edit-experiment',
- 'experiment_id': experiment_id})
+ experiment = request.airavata_client.getExperiment(request.authz_token, experiment_id)
+ applicationInterface = request.airavata_client.getApplicationInterface(request.authz_token, experiment.executionId)
+ app_module_id = applicationInterface.applicationModules[0]
+ context = {
+ 'bundle_name': 'edit-experiment',
+ 'experiment_id': experiment_id,
+ 'app_module_id': app_module_id,
+ }
+ template_path = 'django_airavata_workspace/edit_experiment.html'
+ # Apply a custom application template if it exists
+ custom_template_path, custom_context = get_custom_template(request, app_module_id)
+ if custom_template_path is not None:
+ logger.debug(f"Applying custom application template {custom_template_path}")
+ template_path = custom_template_path
+ context.update(custom_context)
+
+ return render(request, template_path, context)
+
+
+def get_custom_template(request, app_module_id):
+ template_path = None
+ context = {}
+ query = models.ApplicationTemplate.objects.filter(application_module_id=app_module_id)
+ if query.exists():
+ application_template = query.get()
+ template_path = application_template.template_path
+ for context_processor in application_template.context_processors.all():
+ context_processor = import_string(context_processor.callable_path)
+ context.update(context_processor(request))
+ return template_path, context
@login_required