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 <[email protected]> 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
