From 6faf124d7f7e1c2a96ffd455454def6e47da1201 Mon Sep 17 00:00:00 2001 From: jgu2 Date: Mon, 16 Sep 2024 23:43:20 -0600 Subject: [PATCH 01/19] Add solver list --- calliope_app/api/admin.py | 4 +- calliope_app/api/forms.py | 15 + ...move_computeenvironment_solver_and_more.py | 23 + calliope_app/api/models/engage.py | 8 +- calliope_app/api/urls.py | 2 + calliope_app/api/views/outputs.py | 27 +- calliope_app/client/static/js/add_run.js | 442 +++++++++--------- calliope_app/client/templates/add_run.html | 46 +- calliope_app/client/widgets.py | 44 ++ calliope_app/compose/Dockerfile | 2 +- 10 files changed, 373 insertions(+), 240 deletions(-) create mode 100644 calliope_app/api/forms.py create mode 100644 calliope_app/api/migrations/0068_remove_computeenvironment_solver_and_more.py create mode 100644 calliope_app/client/widgets.py diff --git a/calliope_app/api/admin.py b/calliope_app/api/admin.py index da0b5c06..276c9cec 100644 --- a/calliope_app/api/admin.py +++ b/calliope_app/api/admin.py @@ -9,11 +9,13 @@ Scenario_Loc_Tech, Scenario_Param, Job_Meta, Carrier from api.models.outputs import Run from api.models.engage import User_Profile, ComputeEnvironment +from api.forms import ComputeEnvironmentModelForm class ComputeEnvironmentAdmin(admin.ModelAdmin): + form = ComputeEnvironmentModelForm filter_horizontal = ("users",) - list_display = ['id', 'name', 'full_name', 'is_default', 'solver', 'ncpu', 'memory', 'type', '_users'] + list_display = ['id', 'name', 'full_name', 'is_default', 'solvers', 'ncpu', 'memory', 'type', '_users'] @staticmethod def _users(instance): diff --git a/calliope_app/api/forms.py b/calliope_app/api/forms.py new file mode 100644 index 00000000..d300bf2b --- /dev/null +++ b/calliope_app/api/forms.py @@ -0,0 +1,15 @@ + +from django import forms + +from client.widgets import JSONEditorWidget + +from api.models.engage import ComputeEnvironment + + +class ComputeEnvironmentModelForm(forms.ModelForm): + class Meta: + model = ComputeEnvironment + fields = '__all__' + widgets = { + 'solvers': JSONEditorWidget() + } diff --git a/calliope_app/api/migrations/0068_remove_computeenvironment_solver_and_more.py b/calliope_app/api/migrations/0068_remove_computeenvironment_solver_and_more.py new file mode 100644 index 00000000..4d548fd6 --- /dev/null +++ b/calliope_app/api/migrations/0068_remove_computeenvironment_solver_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.15 on 2024-09-17 03:26 + +import api.models.engage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0067_auto_20240613_1706'), + ] + + operations = [ + migrations.RemoveField( + model_name='computeenvironment', + name='solver', + ), + migrations.AddField( + model_name='computeenvironment', + name='solvers', + field=models.JSONField(default=api.models.engage.default_solvers), + ), + ] diff --git a/calliope_app/api/models/engage.py b/calliope_app/api/models/engage.py index e0b19b64..dc1bd103 100644 --- a/calliope_app/api/models/engage.py +++ b/calliope_app/api/models/engage.py @@ -102,6 +102,10 @@ def activate(cls, activation_uuid): return True +def default_solvers(): + return {"appsi_highs": "HiGHS", "cbc": "CBC"} + + class ComputeEnvironment(models.Model): ENV_TYPES = [ @@ -114,11 +118,11 @@ class ComputeEnvironment(models.Model): full_name = models.CharField(max_length=120) is_default = models.BooleanField(default=False) type = models.CharField(max_length=60, choices=ENV_TYPES) - solver = models.CharField(max_length=60, null=True, blank=True) ncpu = models.PositiveSmallIntegerField(null=True, blank=True) memory = models.PositiveSmallIntegerField(null=True, blank=True) cmd = models.TextField(blank=True, null=True) users = models.ManyToManyField(User, related_name="compute_environments", blank=True) + solvers = models.JSONField(default=default_solvers) class Meta: db_table = "compute_environments" @@ -141,4 +145,4 @@ class Meta: verbose_name_plural = "[Admin] Request Rate Limits" def __str__(self): - return f"{self.year}, {self.month}, {self.total}" \ No newline at end of file + return f"{self.year}, {self.month}, {self.total}" diff --git a/calliope_app/api/urls.py b/calliope_app/api/urls.py index e8dfb753..8431adb9 100644 --- a/calliope_app/api/urls.py +++ b/calliope_app/api/urls.py @@ -133,6 +133,8 @@ path('upload_outputs/', outputs_views.upload_outputs, name='upload_outputs'), + path('solvers/', outputs_views.solvers, + name="solvers"), # Bulk Data path('upload_locations/', diff --git a/calliope_app/api/views/outputs.py b/calliope_app/api/views/outputs.py index 8515cc73..522e158c 100644 --- a/calliope_app/api/views/outputs.py +++ b/calliope_app/api/views/outputs.py @@ -3,16 +3,13 @@ import io import logging import os -import shutil import zipfile -import sys from re import match from datetime import datetime, timedelta from urllib.parse import urljoin import requests import pandas as pd -import pint from celery import current_app,chain from django.views.decorators.csrf import csrf_protect @@ -30,7 +27,7 @@ from api.models.calliope import Abstract_Tech, Abstract_Tech_Param, Parameter, Run_Parameter from api.models.configuration import ( Model, ParamsManager, User_File, Location, Technology, - Tech_Param, Loc_Tech, Loc_Tech_Param, Timeseries_Meta, Carrier, Scenario_Param + Tech_Param, Loc_Tech, Loc_Tech_Param, Timeseries_Meta, Carrier ) from api.models.engage import ComputeEnvironment @@ -38,11 +35,19 @@ from batch.managers import AWSBatchJobManager from taskmeta.models import CeleryTask, BatchTask, batch_task_status -from calliope_app.celery import app - logger = logging.getLogger(__name__) +@csrf_protect +def solvers(request): + env_name = request.GET.get("env_name", None) + if not env_name: + env_name = "default" + env = ComputeEnvironment.objects.get(name=env_name) + payload = env.solvers + return HttpResponse(json.dumps(payload), content_type="application/json") + + @csrf_protect def build(request): """ @@ -156,13 +161,13 @@ def build(request): ) inputs_path = inputs_path.lower().replace(" ", "-") os.makedirs(inputs_path, exist_ok=True) - + run.run_options = [] for id in parameters.keys(): run_parameter= Run_Parameter.objects.get(pk=int(id)) run.run_options.append({'root':run_parameter.root,'name':run_parameter.name,'value':parameters[id]}) - - # Celery task + + # Celery task async_result = build_model.apply_async( kwargs={ "inputs_path": inputs_path, @@ -300,7 +305,7 @@ def optimize(request): r.batch_job.status = batch_task_status.FAILED r.batch_job.save() r.save() - + if not all_complete: payload = { "status": "BLOCKED", @@ -344,7 +349,7 @@ def optimize(request): else: logger.info("Found a subsequent gradient model for year %s but it was not built.",next_run.year) break - + # Unknown environment, not supported else: raise Exception("Failed to submit job, unknown compute environment") diff --git a/calliope_app/client/static/js/add_run.js b/calliope_app/client/static/js/add_run.js index 10998443..dd42fb63 100644 --- a/calliope_app/client/static/js/add_run.js +++ b/calliope_app/client/static/js/add_run.js @@ -1,209 +1,233 @@ -$(document).ready(function () { - - add_run_precheck(); - - $('#master-cancel').removeClass('hide'); - $('#master-save').removeClass('hide'); - - $('#master-cancel').on('click', function () { - var model_uuid = $('#header').data('model_uuid'); - window.location = '/' + model_uuid + '/runs/'; - }); - - $('#master-save').on('click', function () { - var model_uuid = $('#header').data('model_uuid'), - scenario_id = $("#scenario").data('scenario_id'), - start_date = $('#start_date').val(), - end_date = $('#end_date').val(), - cluster = $('#cluster').is(":checked"), - manual = $('#manual').is(":checked"), - timestep = $('#timestep').val(), - sd = new Date(start_date), - ed = new Date(end_date), - run_env = $('#run-environment option:selected').text(), - years = $('#years').val(), - notes = $('#notes').val(); - - var parameters = {}; - $('#run_parameters .parameter-row').each(function() { - var paramId = $(this).data('param-id'); - var value = $(this).find('.run-parameter-value').val(); - parameters[paramId] = value; - }); - console.log(parameters); - - - // fix timezone issues - sd = new Date(sd.getTime() + sd.getTimezoneOffset() * 60000); - ed = new Date(ed.getTime() + ed.getTimezoneOffset() * 60000); - - var validated = true; - if (scenario_id == undefined) { - alert('Must choose a Scenario') - validated = false; - } else if (!(sd & ed)) { - alert('Must select a date range below.'); - validated = false; - } else if (sd > ed) { - alert('Start date can not be later then the end date.'); - validated = false; - } else if (sd.getFullYear() != ed.getFullYear()) { - alert('Start date and end date must occur within the same year') - validated = false; - }; - - if (validated) { - $('#master-save').prop('disabled', true); - - $.ajax({ - url: '/' + LANGUAGE_CODE + '/api/build/', - contentType: 'application/json', // Specify that you're sending JSON - data: { - 'model_uuid': model_uuid, - 'scenario_id': scenario_id, - 'start_date': start_date, - 'end_date': end_date, - 'cluster': cluster, - 'manual': manual, - 'timestep': timestep, - 'run_env': run_env, - 'years': years, - 'notes': notes, - 'parameters': JSON.stringify(parameters) - }, - dataType: 'json', - success: function (data) { - if (data['status'] == 'Success') { - window.location = '/' + model_uuid + '/runs/'; - } else { - $('#build-error').html(data['message']); - $('#master-save').prop('disabled', false); - }; - } - }); - }; - }); - - // Automatically deactivate clustering if manual is enabled. - $('#manual').on('click', function () { - if ($('#manual').is(":checked")) { - $('#cluster').prop('checked', false); - } - }); - -}); - - -function add_run_precheck() { - var model_uuid = $('#header').data('model_uuid'), - scenario_id = $("#scenario").data('scenario_id'); - $.ajax({ - url: '/' + LANGUAGE_CODE + '/component/add_run_precheck/', - data: { - 'model_uuid': model_uuid, - 'scenario_id': scenario_id, - }, - dataType: 'json', - success: function (data) { - $('#add_run_precheck').html(data['html']); - render_gantt(); - activate_tiles(); - } - }); -}; - -function activate_tiles() { - $('.selection_tile').on('click', function () { - var start_date = $(this).data('start_date'), - end_date = $(this).data('end_date'); - $('.selection_tile').removeClass('btn-outline-primary') - $(this).addClass('btn-outline-primary') - $('#start_date').val(start_date); - $('#end_date').val(end_date); - }) -} - -function render_gantt() { - - var data = $('#timeseries_gantt').data('timeseries'); - - var margin = { top: 40, right: 40, bottom: 20, left: 40 }, - width = $('#timeseries_gantt').width() - margin.left - margin.right, - bar_height = 16 - height = (bar_height + 4) * data.length; - - // Prep data - var parseDate = d3.timeParse("%m/%d/%Y, %H:%M:%S"); - data.forEach(function (d) { - d.node = d[0] - d.parameter = d[1] - d.start_date = parseDate(d[2]); - d.end_date = parseDate(d[3]); - }); - - // X Axis - var start_date = d3.min(data, function (d) { return d.start_date }), - end_date = d3.max(data, function (d) { return d.end_date }); - var x = d3.scaleTime() - .domain([start_date, end_date]) - .range([0, width]); - var xAxis = d3.axisTop() - .scale(x); - - // Y Axis - var y = d3.scaleLinear() - .domain([data.length, 0]) - .range([height, 0]); - - - // Draw - var svg = d3.select("#timeseries_gantt").append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - // Define the div for the tooltip - var tooltip = d3.select("body").append("div") - .attr("class", "tooltip") - .style("background-color", "white") - .style("border", "solid 3px black") - .style("padding", "5px") - .style("opacity", 0); - - svg.append("g") - .attr("class", "x axis") - .style("font-size", "1.2em") - .call(xAxis) - var g = svg.selectAll() - .data(data).enter().append("g"); - - g.append("rect") - .attr("height", bar_height) - .attr("width", function (d) { return x(end_date) - x(start_date); }) - .attr("x", function (d) { return x(start_date); }) - .attr("y", function (d, i) { return y(i) + (bar_height / 2); }) - .style("fill", "red") - .style("opacity", "0.2"); - - g.append("rect") - .attr("height", bar_height) - .attr("width", function (d) { return x(d.end_date) - x(d.start_date); }) - .attr("x", function (d) { return x(d.start_date); }) - .attr("y", function (d, i) { return y(i) + (bar_height / 2); }) - .style("fill", "green") - .on("mouseover", function (d) { - tooltip.transition() - .duration(200) - .style("opacity", 1); - tooltip.html("" + d.node + "
" + d.parameter) - .style("left", (d3.event.pageX - 100) + "px") - .style("top", (d3.event.pageY - 50) + "px"); - }) - .on("mouseout", function (d) { - tooltip.transition() - .duration(500) - .style("opacity", 0); - }); - -}; +$(document).ready(function () { + + add_run_precheck(); + + $('#master-cancel').removeClass('hide'); + $('#master-save').removeClass('hide'); + + $('#master-cancel').on('click', function () { + var model_uuid = $('#header').data('model_uuid'); + window.location = '/' + model_uuid + '/runs/'; + }); + + $('#master-save').on('click', function () { + var model_uuid = $('#header').data('model_uuid'), + scenario_id = $("#scenario").data('scenario_id'), + start_date = $('#start_date').val(), + end_date = $('#end_date').val(), + cluster = $('#cluster').is(":checked"), + manual = $('#manual').is(":checked"), + timestep = $('#timestep').val(), + sd = new Date(start_date), + ed = new Date(end_date), + run_env = $('#run-environment option:selected').text(), + years = $('#years').val(), + notes = $('#notes').val(); + + var parameters = {}; + $('#run_parameters .parameter-row').each(function() { + var paramId = $(this).data('param-id'); + var value = $(this).find('.run-parameter-value').val(); + parameters[paramId] = value; + }); + + // fix timezone issues + sd = new Date(sd.getTime() + sd.getTimezoneOffset() * 60000); + ed = new Date(ed.getTime() + ed.getTimezoneOffset() * 60000); + + var validated = true; + if (scenario_id == undefined) { + alert('Must choose a Scenario') + validated = false; + } else if (!(sd & ed)) { + alert('Must select a date range below.'); + validated = false; + } else if (sd > ed) { + alert('Start date can not be later then the end date.'); + validated = false; + } else if (sd.getFullYear() != ed.getFullYear()) { + alert('Start date and end date must occur within the same year') + validated = false; + }; + + if (validated) { + $('#master-save').prop('disabled', true); + + $.ajax({ + url: '/' + LANGUAGE_CODE + '/api/build/', + contentType: 'application/json', // Specify that you're sending JSON + data: { + 'model_uuid': model_uuid, + 'scenario_id': scenario_id, + 'start_date': start_date, + 'end_date': end_date, + 'cluster': cluster, + 'manual': manual, + 'timestep': timestep, + 'run_env': run_env, + 'years': years, + 'notes': notes, + 'parameters': JSON.stringify(parameters) + }, + dataType: 'json', + success: function (data) { + if (data['status'] == 'Success') { + window.location = '/' + model_uuid + '/runs/'; + } else { + $('#build-error').html(data['message']); + $('#master-save').prop('disabled', false); + }; + } + }); + }; + }); + + // Automatically deactivate clustering if manual is enabled. + $('#manual').on('click', function () { + if ($('#manual').is(":checked")) { + $('#cluster').prop('checked', false); + } + }); + + var env_name = $(this).val(); + set_solvers(env_name); + + $("#run-environment").change(function () { + var env_name = $(this).val(); + set_solvers(env_name); + }); + +}); + + +function set_solvers(env_name) { + $.ajax({ + url: '/' + LANGUAGE_CODE + '/api/solvers/', + data: { + 'env_name': env_name, + }, + success: function(data) { + var solvers = $('#run-solvers'); + solvers.empty(); + $.each(data, function(key, value) { + solvers.append($('').attr('value', key).text(value)); + }); + } + }); +} + + +function add_run_precheck() { + var model_uuid = $('#header').data('model_uuid'), + scenario_id = $("#scenario").data('scenario_id'); + $.ajax({ + url: '/' + LANGUAGE_CODE + '/component/add_run_precheck/', + data: { + 'model_uuid': model_uuid, + 'scenario_id': scenario_id, + }, + dataType: 'json', + success: function (data) { + $('#add_run_precheck').html(data['html']); + render_gantt(); + activate_tiles(); + } + }); +}; + + +function activate_tiles() { + $('.selection_tile').on('click', function () { + var start_date = $(this).data('start_date'), + end_date = $(this).data('end_date'); + $('.selection_tile').removeClass('btn-outline-primary') + $(this).addClass('btn-outline-primary') + $('#start_date').val(start_date); + $('#end_date').val(end_date); + }) +} + +function render_gantt() { + + var data = $('#timeseries_gantt').data('timeseries'); + + var margin = { top: 40, right: 40, bottom: 20, left: 40 }, + width = $('#timeseries_gantt').width() - margin.left - margin.right, + bar_height = 16 + height = (bar_height + 4) * data.length; + + // Prep data + var parseDate = d3.timeParse("%m/%d/%Y, %H:%M:%S"); + data.forEach(function (d) { + d.node = d[0] + d.parameter = d[1] + d.start_date = parseDate(d[2]); + d.end_date = parseDate(d[3]); + }); + + // X Axis + var start_date = d3.min(data, function (d) { return d.start_date }), + end_date = d3.max(data, function (d) { return d.end_date }); + var x = d3.scaleTime() + .domain([start_date, end_date]) + .range([0, width]); + var xAxis = d3.axisTop() + .scale(x); + + // Y Axis + var y = d3.scaleLinear() + .domain([data.length, 0]) + .range([height, 0]); + + + // Draw + var svg = d3.select("#timeseries_gantt").append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + + // Define the div for the tooltip + var tooltip = d3.select("body").append("div") + .attr("class", "tooltip") + .style("background-color", "white") + .style("border", "solid 3px black") + .style("padding", "5px") + .style("opacity", 0); + + svg.append("g") + .attr("class", "x axis") + .style("font-size", "1.2em") + .call(xAxis) + var g = svg.selectAll() + .data(data).enter().append("g"); + + g.append("rect") + .attr("height", bar_height) + .attr("width", function (d) { return x(end_date) - x(start_date); }) + .attr("x", function (d) { return x(start_date); }) + .attr("y", function (d, i) { return y(i) + (bar_height / 2); }) + .style("fill", "red") + .style("opacity", "0.2"); + + g.append("rect") + .attr("height", bar_height) + .attr("width", function (d) { return x(d.end_date) - x(d.start_date); }) + .attr("x", function (d) { return x(d.start_date); }) + .attr("y", function (d, i) { return y(i) + (bar_height / 2); }) + .style("fill", "green") + .on("mouseover", function (d) { + tooltip.transition() + .duration(200) + .style("opacity", 1); + tooltip.html("" + d.node + "
" + d.parameter) + .style("left", (d3.event.pageX - 100) + "px") + .style("top", (d3.event.pageY - 50) + "px"); + }) + .on("mouseout", function (d) { + tooltip.transition() + .duration(500) + .style("opacity", 0); + }); + +}; diff --git a/calliope_app/client/templates/add_run.html b/calliope_app/client/templates/add_run.html index 9433d66f..5c5975e4 100644 --- a/calliope_app/client/templates/add_run.html +++ b/calliope_app/client/templates/add_run.html @@ -18,9 +18,9 @@ background-color: #192733; border-radius: 10px; padding: 10px; - left: 0%; + left: 0%; margin-left: 10px; /* Offset it by 10 pixels to the right */ - transform: translateY(10px); + transform: translateY(10px); } .hover-text:hover .tooltip-text { @@ -45,7 +45,7 @@ {% endblock %} {% block config_runs %}
  {% trans "Runs" %}
{% endblock %} -{% block config_5_sync %}{% endblock %} +{% block config_5_sync %}{% endblock %} {% block config_5_tab %}tab-active{% endblock %} {% block content %} @@ -58,7 +58,7 @@
{% trans "Select" %} {% trans "a time period" %} {% trans "below" %}

- +
{% trans "Start Date" %}    @@ -87,22 +87,22 @@
{% trans "Gradient Years" %}
{% trans "Run Options" %}:
-
+
{% trans "Enable Clustered Run:" %}
-
+
{% trans "Enable Manual Run:" %}
-
+
{% trans "Run Timestep" %}
- +
- -
{% trans "Select Run Environment:" %}
+ +
{% trans "Run Environment" %}
-
- {% trans "Run Notes" %}
- -
-
+ {% for param in parameters %} + {% if param.run_parameter.name == 'solver' %} +
+
+
{% trans "Run Solver" %}
+
+
+ +
+
+ {% else %}
@@ -127,7 +134,7 @@
{% if param.run_parameter.choices %} - {% for choice in param.run_parameter.choices %} {% endfor %} @@ -137,8 +144,15 @@
+ {% trans "Run Notes" %}
+ +
+

diff --git a/calliope_app/client/widgets.py b/calliope_app/client/widgets.py new file mode 100644 index 00000000..8111b8d7 --- /dev/null +++ b/calliope_app/client/widgets.py @@ -0,0 +1,44 @@ +import json + +from django import forms +from django.utils.safestring import mark_safe + +class JSONEditorWidget(forms.Textarea): + class Media: + css = { + 'all': ('https://cdn.jsdelivr.net/npm/jsoneditor@9.9.0/dist/jsoneditor.min.css',) + } + js = ('https://cdn.jsdelivr.net/npm/jsoneditor@9.9.0/dist/jsoneditor.min.js',) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def render(self, name, value, attrs=None, renderer=None): + editor_id = attrs.get('id', name) + editor_script = f""" + +
+ + + """ + return mark_safe(editor_script) diff --git a/calliope_app/compose/Dockerfile b/calliope_app/compose/Dockerfile index b99c1482..00f02f50 100644 --- a/calliope_app/compose/Dockerfile +++ b/calliope_app/compose/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-slim-bookworm as app +FROM python:3.8-slim-bookworm AS app # set environment variables ENV LC_ALL=C.UTF-8 From d4a33802b6ac77d8365b3ec70f0cedc95b50e448 Mon Sep 17 00:00:00 2001 From: jgu2 Date: Tue, 17 Sep 2024 17:31:18 -0600 Subject: [PATCH 02/19] Manage sover access via Django admin --- calliope_app/api/models/engage.py | 13 +++++++++++- calliope_app/api/views/outputs.py | 25 ++++++++++++++++++++++-- calliope_app/client/static/js/add_run.js | 4 +++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/calliope_app/api/models/engage.py b/calliope_app/api/models/engage.py index dc1bd103..49674176 100644 --- a/calliope_app/api/models/engage.py +++ b/calliope_app/api/models/engage.py @@ -103,7 +103,18 @@ def activate(cls, activation_uuid): def default_solvers(): - return {"appsi_highs": "HiGHS", "cbc": "CBC"} + return [ + { + "name": "appsi_highs", + "pretty_name": "HiGHS", + "order": 1 + }, + { + "name": "cbc", + "pretty_name": "CBC", + "order": 2 + } + ] class ComputeEnvironment(models.Model): diff --git a/calliope_app/api/views/outputs.py b/calliope_app/api/views/outputs.py index 522e158c..72eb2166 100644 --- a/calliope_app/api/views/outputs.py +++ b/calliope_app/api/views/outputs.py @@ -43,8 +43,29 @@ def solvers(request): env_name = request.GET.get("env_name", None) if not env_name: env_name = "default" - env = ComputeEnvironment.objects.get(name=env_name) - payload = env.solvers + + flag = True + try: + env = ComputeEnvironment.objects.get(name=env_name) + except Cambium.Environment.DoesNotExist: + flag = False + + if (not flag) or (not env.solvers) or (not isinstance(env.solvers, list)): + payload = [ + { + "name": "appsi_highs", + "pretty_name": "HiGHS", + "order": 1 + }, + { + "name": "cbc", + "pretty_name": "CBC", + "order": 2 + } + ] + else: + payload = sorted(env.solvers, key=lambda x: x["order"]) + return HttpResponse(json.dumps(payload), content_type="application/json") diff --git a/calliope_app/client/static/js/add_run.js b/calliope_app/client/static/js/add_run.js index dd42fb63..cce1ff72 100644 --- a/calliope_app/client/static/js/add_run.js +++ b/calliope_app/client/static/js/add_run.js @@ -109,7 +109,9 @@ function set_solvers(env_name) { success: function(data) { var solvers = $('#run-solvers'); solvers.empty(); - $.each(data, function(key, value) { + $.each(data, function(index, item) { + var key = item.name; + var value = item.pretty_name; solvers.append($('').attr('value', key).text(value)); }); } From 2b2f934e7e455e3da8fa55f0d406cb8633e1072a Mon Sep 17 00:00:00 2001 From: jgu2 Date: Tue, 17 Sep 2024 18:01:01 -0600 Subject: [PATCH 03/19] Update default solver management --- calliope_app/api/engage.py | 22 ++++++++++++++++++++++ calliope_app/api/models/engage.py | 15 +++------------ calliope_app/api/views/outputs.py | 26 ++++++++++++-------------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/calliope_app/api/engage.py b/calliope_app/api/engage.py index 086d8ed9..3ee87c8d 100644 --- a/calliope_app/api/engage.py +++ b/calliope_app/api/engage.py @@ -6,6 +6,28 @@ from django.core.mail.message import sanitize_address +ENGAGE_SOLVERS = [ + { + "name": "appsi_highs", + "pretty_name": "HiGHS", + "order": 1, + "is_active": True + }, + { + "name": "cbc", + "pretty_name": "CBC", + "order": 2, + "is_active": True + }, + { + "name": "amplxpress", + "pretty_name": "Xpress", + "order": 3, + "is_active": False + } +] + + def aws_ses_configured(): """ Check the configuration of AWS SES settings diff --git a/calliope_app/api/models/engage.py b/calliope_app/api/models/engage.py index 49674176..822998a0 100644 --- a/calliope_app/api/models/engage.py +++ b/calliope_app/api/models/engage.py @@ -9,6 +9,8 @@ from django.urls import reverse from django.utils.html import mark_safe +from api.engage import ENGAGE_SOLVERS + logger = logging.getLogger(__name__) @@ -103,18 +105,7 @@ def activate(cls, activation_uuid): def default_solvers(): - return [ - { - "name": "appsi_highs", - "pretty_name": "HiGHS", - "order": 1 - }, - { - "name": "cbc", - "pretty_name": "CBC", - "order": 2 - } - ] + return ENGAGE_SOLVERS class ComputeEnvironment(models.Model): diff --git a/calliope_app/api/views/outputs.py b/calliope_app/api/views/outputs.py index 72eb2166..5140d102 100644 --- a/calliope_app/api/views/outputs.py +++ b/calliope_app/api/views/outputs.py @@ -31,6 +31,7 @@ ) from api.models.engage import ComputeEnvironment +from api.engage import ENGAGE_SOLVERS from api.utils import zip_folder, initialize_units, convert_units, noconv_units from batch.managers import AWSBatchJobManager from taskmeta.models import CeleryTask, BatchTask, batch_task_status @@ -47,24 +48,21 @@ def solvers(request): flag = True try: env = ComputeEnvironment.objects.get(name=env_name) - except Cambium.Environment.DoesNotExist: + except ComputeEnvironment.DoesNotExist: flag = False if (not flag) or (not env.solvers) or (not isinstance(env.solvers, list)): - payload = [ - { - "name": "appsi_highs", - "pretty_name": "HiGHS", - "order": 1 - }, - { - "name": "cbc", - "pretty_name": "CBC", - "order": 2 - } - ] + solvers = ENGAGE_SOLVERS else: - payload = sorted(env.solvers, key=lambda x: x["order"]) + solvers = env.solvers + + candidates = [] + for solver in solvers: + is_active = solver.get("is_active", "false") + if is_active == "true": + candidates.append(solver) + + payload = sorted(candidates, key=lambda x: x["order"]) return HttpResponse(json.dumps(payload), content_type="application/json") From 489e63a37fcabe90f7018296c668e1915b57a15d Mon Sep 17 00:00:00 2001 From: jgu2 Date: Tue, 17 Sep 2024 18:02:48 -0600 Subject: [PATCH 04/19] Adjust ace editor height --- calliope_app/client/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calliope_app/client/widgets.py b/calliope_app/client/widgets.py index 8111b8d7..962d4df6 100644 --- a/calliope_app/client/widgets.py +++ b/calliope_app/client/widgets.py @@ -21,7 +21,7 @@ def render(self, name, value, attrs=None, renderer=None): background: #79aec8 !important; }} -
+