Skip to content

Commit

Permalink
Fixes and QoL changes for the Webserver Configurator (#394)
Browse files Browse the repository at this point in the history
* refactoring: ajax for + button in configurator

* selection list for marketplaces

* implemented #358

* api health check button

* fixed api state button for multiple clicks

* polling for api health, agent_monitoring

* implemented #369

* dynamic marketplace list is not part of this pr

* implemented review feedback
  • Loading branch information
felix-20 authored Apr 6, 2022
1 parent 545825a commit c4fef70
Show file tree
Hide file tree
Showing 15 changed files with 186 additions and 87 deletions.
11 changes: 11 additions & 0 deletions docker/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,16 @@ async def remove_container(id: str) -> JSONResponse:
else:
return JSONResponse(vars(container_info))


@app.get('/api_health')
async def check_if_api_is_available() -> JSONResponse:
"""
This is a route you can call to see if the API is available.
Returns:
JSONResponse: A json containing only a `status` field.
"""
return JSONResponse({'status': 'I am alive'})


if __name__ == '__main__':
uvicorn.run('app:app', host='0.0.0.0', port=8000)
24 changes: 20 additions & 4 deletions webserver/alpha_business_app/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,26 @@ def _params_for_config(self) -> dict:
Returns:
dict: contains all current configuration objects, the current config and this config as dict if it exists.
"""
return {'all_configurations': Config.objects.all(),
return {
'all_configurations': Config.objects.all(),
'config': self.wanted_config,
'config_dict': self.wanted_config.as_dict() if self.wanted_config else None}
'config_dict': self.wanted_config.as_dict() if self.wanted_config else None,
**self._params_for_selection()
}

def _params_for_selection(self) -> dict:
# TODO: implement the selection parameters here
# import recommerce.market.circular.circular_sim_market as circular_market
# circular_market_places = list(set(filter(lambda class_name: class_name.startswith('CircularEconomy'), dir(circular_market))))
circular_market_places = ['CircularEconomyMonopolyScenario',
'CircularEconomyRebuyPrice',
'CircularEconomyRebuyPriceMonopolyScenario',
'CircularEconomyRebuyPriceOneCompetitor']
circular_market_places = [('recommerce.market.circular.circular_sim_market.' + market, market) for market in circular_market_places]
return {
'selections': {
'tasks': [('training', 'training'), ('agent_monitoring', 'agent_monitoring'), ('exampleprinter', 'exampleprinter')],
'marketplaces': circular_market_places}}

def _render_default(self) -> HttpResponse:
"""
Expand Down Expand Up @@ -253,7 +270,7 @@ def _pre_fill(self) -> HttpResponse:
merger = ConfigMerger()
final_dict, error_dict = merger.merge_config_objects(post_request['config_id'])
return render(self.request, self.view_to_render,
{'prefill': final_dict, 'error_dict': error_dict, 'all_configurations': Config.objects.all()})
{'prefill': final_dict, 'error_dict': error_dict, 'all_configurations': Config.objects.all(), **self._params_for_selection()})

def _remove(self) -> HttpResponse:
"""
Expand Down Expand Up @@ -297,7 +314,6 @@ def _start(self) -> HttpResponse:
print('the new container has the same id, as another container')
self.message = ['error', 'please try again']
return self._remove()
print('after post request')
# get all necessary parameters for container object
container_name = self.request.POST['experiment_name']
container_name = container_name if container_name != '' else response['id'][:10]
Expand Down
6 changes: 1 addition & 5 deletions webserver/alpha_business_app/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,7 @@ def capitalize(word: str) -> str:


def to_config_class_name(name: str) -> str:
# replace all brackets
class_name = name.replace('(', '').replace(')', '')
# remove all whitespaces:
class_name = ''.join([capitalize(x) for x in class_name.split(' ')])
return ''.join([capitalize(x) for x in class_name.split('_')]) + 'Config'
return ''.join([capitalize(x) for x in name.split('_')]) + 'Config'


def remove_none_values_from_dict(dict_with_none_values: dict) -> dict:
Expand Down
29 changes: 29 additions & 0 deletions webserver/alpha_business_app/static/css/buttons.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,32 @@
.bc-error-field {
border-color: rgb(219, 1, 56) !important;
}

.bc-outline-lightgray {
color: rgb(125, 125, 125) !important;
border-color: rgb(125, 125, 125) !important;
}
.bc-outline-lightgray:hover {
color: #fff !important;
background-color: rgb(125, 125, 125);
border-color: rgb(125, 125, 125);
}

.bc-lightgray {
background-color: rgb(125, 125, 125) !important;
border-color: rgb(125, 125, 125) !important;
}

.bc-lightgreen {
background-color: rgb(69, 211, 4) !important;
border-color: rgb(69, 211, 4) !important;
}

.bc-red {
background-color: rgb(219, 1, 56) !important;
border-color: rgb(219, 1, 56) !important;
}

.bc-xs {
height: min-content !important;
}
52 changes: 37 additions & 15 deletions webserver/alpha_business_app/static/js/custom.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
function uuidv4() {
// https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}


$(document).ready(function() {
$(".add-more").click(function(){
var html = $("<div/>").html($(".copy").html());
var target_replaces = html.find('.target-replace');
var uuid = uuidv4();
$(target_replaces[0]).attr("data-bs-target", "#collapseAgent" + uuid);
$(target_replaces[1]).attr("id", "collapseAgent" + uuid);
$(".after-add-more").after(html);
$("button.add-more").click(function () {
// adds the return value of the ajax call (html) before the element.
var self = $(this)
$.ajax({url: self.data("url"),
success: function (data) {
self.before(data)
}
});
});


function updateAPIHealth() {
// replaces the element by the element returned by ajax (html)
var statusButton = $("button.replace-me")
$.ajax({url: statusButton.data("url"),
success: function (data) {
statusButton.replaceWith(data);
}
});
};

// check for API status all 5 seconds
updateAPIHealth();
window.setInterval(function() {updateAPIHealth()}, 5000);

$("select.task-selection").change(function () {
// displays the monitoring options when 'agent_monitoring' is selected
var self = this
if(self.value == "agent_monitoring") {
$(".hide-not-monitoring").each(function () {
$(this).removeClass("d-none")
});
} else {
$(".hide-not-monitoring").each(function () {
$(this).addClass("d-none")
});
}
}).trigger('change');
});
6 changes: 0 additions & 6 deletions webserver/alpha_business_app/tests/test_config_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@ def test_class_name_environment_config(self):
def test_class_name_agents_config(self):
assert 'AgentsConfig' == to_config_class_name('agents')

def test_class_name_rule_based_config(self):
assert 'RuleBasedAgentConfig' == to_config_class_name('Rule_Based Agent')

def test_class_name_q_learing_config(self):
assert 'CERebuyAgentQLearningConfig' == to_config_class_name('CE Rebuy Agent (QLearning)')

def test_class_name_hyperparameter_config(self):
assert 'HyperparameterConfig' == to_config_class_name('hyperparameter')

Expand Down
3 changes: 3 additions & 0 deletions webserver/alpha_business_app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@
path('details/<str:container_id>', views.detail, name='detail'),
path('configurator', views.configurator, name='configurator'),
path('delete_config/<int:config_id>', views.delete_config, name='delete_config'),
# AJAX relevant url's
path('agent', views.agent, name='agent'),
path('api_availability', views.api_availability, name='api_availability')
]
21 changes: 21 additions & 0 deletions webserver/alpha_business_app/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import datetime
from uuid import uuid4

import requests
from django.http import Http404, HttpResponse
from django.shortcuts import render

from .buttons import ButtonHandler
from .forms import UploadFileForm
from .handle_files import handle_uploaded_file
from .handle_requests import DOCKER_API
from .models.config import Config
from .models.container import Container

Expand Down Expand Up @@ -57,3 +62,19 @@ def delete_config(request, config_id) -> HttpResponse:
raise Http404('Config does not exist') from error
button_handler = ButtonHandler(request, view='delete_config.html', wanted_config=wanted_config, rendering_method='config')
return button_handler.do_button_click()


# AJAX relevant views
def agent(request):
return render(request, 'configuration_items/agent.html', {'id': str(uuid4())})


def api_availability(request):
try:
requests.get(f'{DOCKER_API}/api_health', timeout=2)
except requests.exceptions.RequestException:
current_time = datetime.datetime.now().strftime('%H:%M:%S')
return render(request, 'api_buttons/api_health_button.html', {'api_error': f'unavailable {current_time}'})

current_time = datetime.datetime.now().strftime('%H:%M:%S')
return render(request, 'api_buttons/api_health_button.html', {'api_success': f'available {current_time}'})
4 changes: 4 additions & 0 deletions webserver/templates/api_buttons/api_health_button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<button class="btn btn-sm bc-xs mt-3 replace-me {% if api_success %}bc-lightgreen {% elif api_error %}bc-red{% else %}bc-lightgray{% endif %}"
type="button"
data-url="/api_availability">
</button>
6 changes: 5 additions & 1 deletion webserver/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<ul class="navbar-nav w-100">
<li class="nav-item">
<a class="nav-item nav-link" href="/">Home</a>
</li>
Expand All @@ -36,6 +36,10 @@
<li class="nav-item">
<a class="nav-item nav-link" href="/download">Download</a>
</li>
<li class="nav-item d-flex ms-auto">
<a class="nav-item nav-link mr-1">API</a>
{% include "api_buttons/api_health_button.html" %}
</li>
</ul>
</div>
</div>
Expand Down
51 changes: 29 additions & 22 deletions webserver/templates/configuration_items/agent.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
<div id="collapseAgent" class="accordion-collapse collapse target-replace">
<div class="accordion-body">
<div class="row p-2">
<div class="col-6">
name
<div class="accordion-item copy after-add-more">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" aria-expanded="false" data-bs-target="#collapseAgent{{id}}">
Agent
</button>
</h2>
<div id="collapseAgent{{id}}" class="accordion-collapse collapse">
<div class="accordion-body">
<div class="row p-2">
<div class="col-6">
name
</div>
<div class="col-6">
<input class="form-control" type="text" name="environment-agents-name">
</div>
</div>
<div class="col-6">
<input class="form-control" type="text" name="environment-agents-name">
<div class="row p-2">
<div class="col-6">
class
</div>
<div class="col-6">
<input class="form-control" type="text" name="environment-agents-agent_class">
</div>
</div>
</div>
<div class="row p-2">
<div class="col-6">
class
</div>
<div class="col-6">
<input class="form-control" type="text" name="environment-agents-agent_class">
</div>
</div>
<div class="row p-2">
<div class="col-6">
argument
</div>
<div class="col-6">
<input class="form-control" type="text" name="environment-agents-argument">
<div class="row p-2">
<div class="col-6">
argument
</div>
<div class="col-6">
<input class="form-control" type="text" name="environment-agents-argument">
</div>
</div>
</div>
</div>
Expand Down
13 changes: 3 additions & 10 deletions webserver/templates/configuration_items/agents.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,13 @@
<h2 class="accordion-header">
{% load static %}
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseAgentContainer" aria-expanded="false" aria-controls="collapseAgentContainer">
{% if error_dict %} <img src="{% static 'icons/warning.svg' %}" width="18px" title="{{error_dict}}" title="{{error_dict}}"></img> {% endif %}
{% if error_dict %} <img src="{% static 'icons/warning.svg' %}" width="18px" title="{{error_dict}}"></img> {% endif %}
Agents
</button>
</h2>
<div id="collapseAgentContainer" class="accordion-collapse collapse">
<div class="accordion-body">
<div class="accordion-item copy after-add-more">
<h2 class="accordion-header">
<button class="accordion-button collapsed target-replace" type="button" data-bs-toggle="collapse" aria-expanded="false" data-bs-target="#collapseAgent"> <!-- aria-controls="collapseAgent"-->
Agent
</button>
</h2>
{% include "configuration_items/agent.html" %}
</div>
<button class="btn btn-success add-more mt-2" type="button">+</button>
{% include "configuration_items/agent.html" with id=0 %}
<button class="btn bc-outline-lightgreen add-more mt-2" data-url="/agent" type="button">+</button>
</div>
</div>
24 changes: 9 additions & 15 deletions webserver/templates/configuration_items/environment.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,13 @@ <h2 class="accordion-header">
task
</div>
<div class="col-6 p-2">
<select class="form-select {% if error_dict.task %} bc-error-field {% endif %}" name="environment-task">
{% if prefill.task %}
<option selected value="training">training</option>
<option value="monitoring">monitoring</option>
<option value="exampleprinter">exampleprinter</option>
{% else %}
<option selected value="training">training</option>
<option value="monitoring">monitoring</option>
<option value="exampleprinter">exampleprinter</option>
{% endif %}
<select class="form-select task-selection {% if error_dict.task %} bc-error-field {% endif %}"
name="environment-task">
{% include "configuration_items/selection_list.html" with prefill_value=prefill.task selections=selections.tasks %}
</select>
</div>
</div>
<div class="row p-2">
<div class="row p-2 hide-not-monitoring">
<div class="col-6">
{% if error_dict.enable_live_draw %}
<img src="{% static 'icons/warning.svg' %}" width="18px" title="{{error_dict.enable_live_draw}}"></img>
Expand All @@ -42,7 +35,7 @@ <h2 class="accordion-header">
name="environment-enable_live_draw">
</div>
</div>
<div class="row p-2">
<div class="row p-2 hide-not-monitoring">
<div class="col-6">
{% if error_dict.episodes %}
<img src="{% static 'icons/warning.svg' %}" width="18px" title="{{error_dict.episodes}}"></img>
Expand All @@ -54,7 +47,7 @@ <h2 class="accordion-header">
value="{{prefill.episodes}}" name="environment-episodes">
</div>
</div>
<div class="row p-2">
<div class="row p-2 hide-not-monitoring">
<div class="col-6">
{% if error_dict.plot_interval %}
<img src="{% static 'icons/warning.svg' %}" width="18px" title="{{error_dict.plot_interval}}"></img>
Expand All @@ -74,8 +67,9 @@ <h2 class="accordion-header">
marketplace
</div>
<div class="col-6">
<input type="text" class="form-control {% if error_dict.marketplace %} bc-error-field {% endif %}"
value="{{prefill.marketplace}}" name="environment-marketplace">
<select class="form-select {% if error_dict.task %} bc-error-field {% endif %}" name="environment-marketplace">
{% include "configuration_items/selection_list.html" with prefill_value=prefill.marketplace selections=selections.marketplaces %}
</select>
</div>
</div>
{% include "configuration_items/agents.html" with prefill=prefill.agents error_dict=error_dict.agents%}
Expand Down
Loading

0 comments on commit c4fef70

Please sign in to comment.