Skip to content
Snippets Groups Projects
Commit 9f4e6679 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

UI/UX improvements for new tasks and navigation. Fixes.

parent 90cee362
No related branches found
No related tags found
No related merge requests found
......@@ -48,9 +48,9 @@ class Command(BaseCommand):
To change it, head to the <a href="/admin">admin</a> page and edit the <code>Text</code> model.
<br/><br/>
The default installation provides a test user register with email <code>testuser@rosetta.platform</code>
and password <code>testpass</code>, which you can use to login on the menu on the right or using the link
below and give Rosetta a try immediately. If you run with the default docker-compose file (i.e. you just
run <code>rosetta/setup</code>), then you will also have a few demo computing resources you can play with
and password <code>testpass</code>, which you can use to login on the menu on the rightand give Rosetta
a try immediately. If you run with the default docker-compose file (i.e. you just run
<code>rosetta/setup</code>), then you will also have a few demo computing resources you can play with
out-of-the-box, including a small Slurm cluster. Otherwise, you will need to setup your own computing
resources either platform-wide or as user.
</div>
......
......@@ -112,7 +112,7 @@
</div>
<div style="margin-bottom:13px; margin-top: 3px; text-align:center">
<a href="/create_task?task_container_uuid={{ container.uuid }}" class="btn btn-light" style="border: #c0c0c0 1px solid">&nbsp;<i class="fa fa-play" style="color:green"></i></a>
<a href="/create_task?task_container_uuid={{ container.uuid }}&step=two" class="btn btn-light" style="border: #c0c0c0 1px solid">&nbsp;<i class="fa fa-play" style="color:green"></i></a>
</div>
......
......@@ -12,15 +12,24 @@
{% if data.container %}
<h1><a href="/containers">Containers</a> <span style="font-size:18px"> / {{ data.container.name }}</span></h1>
{% else %}
{% if data.mode == 'new_task' %}
<h1>New task</h1>
<hr/>
<h3>Step 1: choose software container.</h3>
<br/>
{% else %}
<h1>Containers</h1>
<hr/>
{% endif %}
<div class="form-filter" style="margin-bottom:20px">
<form action="" method="POST">
<input type="hidden" name="mode" value="{{data.mode}}">
<input type="text" class="form-control" id="search_text" name="search_text" placeholder="Search..." style="width:200px; margin:0; display:inline" value="{{data.search_text}}" autofocus>
<select class="form-control" id="search_type" name="search_type" style="width:120px; margin:0; display:inline">
......
......@@ -11,45 +11,49 @@
<h1>New Task</h1>
<hr>
{% if data.step == 'one' %}
<h3>Step 1: name, container and computing.</h3>
<br/>
{% if data.step == 'two' %}
<h3>Step 2: set task name and computing.</h3>
{% elif data.step == 'three' %}
<h3>Step 3: add authentication and computing details.</h3>
{% endif %}
<br/>
<div style="width:300px; float:left; border: #e0e0e0 solid 1px; margin:10px; background:#f8f8f8; margin-bottom:15px">
<div style="padding:10px; margin-top:5px; text-align:center; border-bottom: {{data.task_container.color}} solid 10px; ">
<a href="?uuid={{ container.uuid }}">{{ data.task_container.name }}</a>&nbsp;
{% if data.task_container.type == 'docker' %}<img src="/static/img/docker-logo.svg" style="height:18px; width:18px; margin-bottom:4px" />{% endif %}
{% if data.task_container.type == 'singularity' %}<img src="/static/img/singularity-logo.svg" style="height:18px; width:18px; margin-bottom:4px" />{% endif %}
</div>
<div style="padding:10px; height: 64px; vertical-align: middle;">
<b>Image:</b> {{ data.task_container.image_name }}<br/>
<b>Version:</b> {{ data.task_container.image_tag }}
</div>
</div>
{% if data.step == 'two' %}
<form action="/create_task/" method="POST">
{% csrf_token %}
<input type="hidden" name="step" value="one" />
<input type="hidden" name="step" value="{{ data.next_step }}" />
<input type="hidden" name="task_container_uuid" value="{{data.task_container.uuid}}">
<div style="width:300px; float:left; border: #e0e0e0 solid 0px; margin:10px; background:#f8f8f8; margin-bottom:15px">
<table class="dashboard" style="max-width:700px">
<table style="max-width:100%; border: #e0e0e0 solid 1px; margin:0">
<tr><td colspan="2"></td></tr>
<tr>
<td><b>Task name </b></td>
<td>
<input type="text" name="task_name" value="" placeholder="" size="23" required />
<input type="text" name="task_name" value="" placeholder="" size="" required />
</td>
</tr>
<tr>
<td><b>Task container</b></td><td>
{% if data.task_container %}
<select name="task_container_uuid">
<option value="{{data.task_container.uuid}}" selected>{{data.task_container.name}} ({{data.task_container.type.title}})</option>
</select>
{% else %}
<select name="task_container_uuid" >
{% for container in data.containers %}
<option value="{{container.uuid}}">{{container.name}} ({{container.type.title}})</option>
{% endfor %}
</select>
{% endif %}
</td>
</tr>
<tr>
<td><b>Computing resource</b></td><td>
<td><b>Computing</b></td><td>
<select name="task_computing_uuid" >
{% for computing in data.computings %}}
<option value="{{ computing.uuid }}">{{ computing.name}}</option>
......@@ -59,38 +63,30 @@
</tr>
<tr>
<td colspan=2 align=center style="padding:20px">
<td colspan=2 align=center style="padding:9px">
<input type="submit" value="Next">
</td>
</tr>
</table>
</div>
</form>
{% elif data.step == 'two' %}
<h3>Step 2: add authentication and computing details</h3>
<br/>
<table class="dashboard" style="max-width:700px">
<tr><td colspan="2"></td></tr>
{% elif data.step == 'three' %}
<div style="width:300px; float:left; border: #e0e0e0 solid 0px; margin:10px; background:#f8f8f8; margin-bottom:15px">
<table style="width:100%; height:126px; border: #e0e0e0 solid 1px; margin:0">
<tr>
<tr valign="bottom">
<td><b>Task name </b></td>
<td>
<input type="text" name="task_name_RECAP" value="{{ data.task_name }}" placeholder="" size="23" disabled />
<input type="text" name="task_name_RECAP" value="{{ data.task_name }}" placeholder="" size="" disabled />
</td>
</tr>
<tr>
<td><b>Task container</b></td><td>
<select name="task_container_uuid_RECAP">
<option value="" selected>{{data.task_container.name}} ({{data.task_container.type.title}})</option>
</select>
</td>
</tr>
<tr>
<td><b>Computing resource</b></td><td>
<tr valign="top">
<td><b>Computing</b></td><td>
<select name="task_computing_uuid_RECAP" >
<option value="">{{ data.task_computing.name}}</option>
</select>
......@@ -98,8 +94,10 @@
</tr>
</table>
</div>
<div style="width:640px; float:left; border: #e0e0e0 solid 0px; margin:10px; background:#f8f8f8; margin-bottom:15px">
<br>
{% if data.task_container.type == 'singularity' and not data.task_container.supports_dynamic_ports %}
<div> <p style="font-size:15px; max-width:700px; margin-bottom:20px; margin-left:5px">
<i class="fa fa-exclamation-triangle" style="color:orange"></i> This container does not support dynamic ports and you are running it with Singularity, without network insulation. This means that if the container port is already occupied, it will not be able to start.
......@@ -114,7 +112,8 @@
<form action="/create_task/" method="POST">
{% csrf_token %}
<input type="hidden" name="step" value="two" />
<input type="hidden" name="task_container_uuid" value="{{data.task_container.uuid}}">
<input type="hidden" name="step" value="{{ data.next_step }}" />
<input type="hidden" name="task_name" value="{{ data.task_name }}" />
<input type="hidden" name="task_container_uuid" value="{{ data.task_container.uuid }}" />
<input type="hidden" name="task_computing_uuid" value="{{ data.task_computing.uuid }}" />
......@@ -180,7 +179,7 @@
</td>
</tr> -->
{% if data.task_computing.type == 'slurm' %}
{% if data.task_computing.access_method == 'slurm+ssh' %}
<tr>
<td><b>Computing options</b></td>
<td>
......@@ -207,6 +206,7 @@
</tr>
</table>
</form>
</div>
{% else %}
......
......@@ -24,18 +24,7 @@
</div>
{% endif %}
</div>
</div>
<div style="display:table-row">
<div class="text-vertical-bottom">
{% if user.is_authenticated %}
<a href="/tasks" class="btn btn-dark btn-lg">Tasks</a>
<a href="/containers" class="btn btn-dark btn-lg">Containers</a>
{% else %}
<a href="/login" class="btn btn-dark btn-lg">Log In</a>
{% endif %}
</div>
</div>
</div>
</header>
{% include "footer.html" %}
......
......@@ -28,7 +28,7 @@
{% if not data.task and not data.tasks %}
<div class="row" style="padding:10px; padding-left:15px">
<i>Nothing here. To create a new task, choose a container from the "containers" menu entry and run it by hitting the play button.</i>
<a href="/create_task">New task...</a>
</div>
{% endif %}
......
......@@ -402,35 +402,12 @@ def create_task(request):
data['profile'] = Profile.objects.get(user=request.user)
data['title'] = 'New Task'
# Step if any
step = request.POST.get('step', None)
# Container uuid if any
container_uuid = request.GET.get('task_container_uuid', None)
if container_uuid:
try:
data['task_container'] = Container.objects.get(uuid=container_uuid, user=request.user)
except Container.DoesNotExist:
data['task_container'] = Container.objects.get(uuid=container_uuid, user=None)
else:
# Get containers
data['containers'] = list(Container.objects.filter(user=None)) + list(Container.objects.filter(user=request.user))
# Get computings
data['computings'] = list(Computing.objects.filter(user=None)) + list(Computing.objects.filter(user=request.user))
# Handle step
if step:
# Task name
task_name = request.POST.get('task_name', None)
if not task_name:
raise ErrorMessage('Missing task name')
data['task_name'] = task_name
# Task container
# Get task container helper function
def get_task_container(request):
task_container_uuid = request.POST.get('task_container_uuid', None)
if not task_container_uuid:
# At the second step the task uuid is set via a GET request
task_container_uuid = request.GET.get('task_container_uuid', None)
try:
task_container = Container.objects.get(uuid=task_container_uuid, user=None)
except Container.DoesNotExist:
......@@ -438,9 +415,10 @@ def create_task(request):
task_container = Container.objects.get(uuid=task_container_uuid, user=request.user)
except Container.DoesNotExist:
raise Exception('Consistency error, container with uuid "{}" does not exists or user "{}" does not have access rights'.format(task_container_uuid, request.user.email))
data['task_container'] = task_container
return task_container
# Task computing
# Get task computing helper function
def get_task_computing(request):
task_computing_uuid = request.POST.get('task_computing_uuid', None)
try:
task_computing = Computing.objects.get(uuid=task_computing_uuid, user=None)
......@@ -450,94 +428,139 @@ def create_task(request):
except Computing.DoesNotExist:
raise Exception('Consistency error, computing with uuid "{}" does not exists or user "{}" does not have access rights'.format(task_computing_uuid, request.user.email))
task_computing.attach_user_conf(request.user)
data['task_computing'] = task_computing
# Handle step one/two
if step == 'one':
# Set step and task uuid
data['step'] = 'two'
elif step == 'two':
# Generate the task uuid
task_uuid = str(uuid.uuid4())
# Create the task object
task = Task(uuid = task_uuid,
user = request.user,
name = task_name,
status = TaskStatuses.created,
container = task_container,
computing = task_computing)
# Add auth
task.auth_user = request.POST.get('auth_user', None)
task.auth_pass = request.POST.get('auth_password', None)
task.access_method = request.POST.get('access_method', None)
task_base_port = request.POST.get('task_base_port', None)
if task_base_port:
task.port = task_base_port
# Checks
if task.auth_pass and len(task.auth_pass) < 6:
raise ErrorMessage('Task password must be at least 6 chars')
# Computing options # TODO: This is hardcoded thinking about Slurm and Singularity
computing_cpus = request.POST.get('computing_cpus', None)
computing_memory = request.POST.get('computing_memory', None)
computing_partition = request.POST.get('computing_partition', None)
extra_binds = request.POST.get('extra_binds', None)
computing_options = {}
if computing_cpus:
try:
int(computing_cpus)
except:
raise Exception('Cannot convert computing_cpus to int')
computing_options['cpus'] = int(computing_cpus)
if computing_memory:
computing_options['memory'] = computing_memory
return task_computing
# Get task name helper function
def get_task_name(request):
task_name = request.POST.get('task_name', None)
if not task_name:
raise ErrorMessage('Missing task name')
return task_name
# Get step if any, check both POST and GET
step = request.POST.get('step', None)
if not step:
step = request.GET.get('step', None)
if computing_partition:
computing_options['partition'] = computing_partition
if computing_options:
task.computing_options = computing_options
# Attach user config to computing
task.computing.attach_user_conf(task.user)
# Set port if not dynamic ports
if not task.container.supports_dynamic_ports:
if task.container.ports:
task.port = task.container.port
# Handle the various steps
if not step:
# Step one is assumed: chose software container
return HttpResponseRedirect('/containers/?mode=new_task')
elif step == 'two':
# Set extra binds if any:
task.extra_binds = extra_binds
# Get software container
data['task_container'] = get_task_container(request)
# Save the task before starting it, or the computing manager will not be able to work properly
task.save()
# Start the task
# List all computing resources
data['computings'] = list(Computing.objects.filter(user=None)) + list(Computing.objects.filter(user=request.user))
data['step'] = 'two'
data['next_step'] = 'three'
elif step == 'three':
# Get software container
data['task_container'] = get_task_container(request)
# Get computing resource
data['task_computing'] = get_task_computing(request)
# Get task name
data['task_name'] = get_task_name(request)
# Set current and next step
data['step'] = 'three'
data['next_step'] = 'last'
elif step == 'last':
# Get software container
data['task_container'] = get_task_container(request)
# Get computing resource
data['task_computing'] = get_task_computing(request)
# Get task name
data['task_name'] = get_task_name(request)
# Generate the task uuid
task_uuid = str(uuid.uuid4())
# Create the task object
task = Task(uuid = task_uuid,
user = request.user,
name = data['task_name'],
status = TaskStatuses.created,
container = data['task_container'],
computing = data['task_computing'])
# Add auth
task.auth_user = request.POST.get('auth_user', None)
task.auth_pass = request.POST.get('auth_password', None)
task.access_method = request.POST.get('access_method', None)
task_base_port = request.POST.get('task_base_port', None)
if task_base_port:
task.port = task_base_port
# Checks
if task.auth_pass and len(task.auth_pass) < 6:
raise ErrorMessage('Task password must be at least 6 chars')
# Computing options # TODO: This is hardcoded thinking about Slurm and Singularity
computing_cpus = request.POST.get('computing_cpus', None)
computing_memory = request.POST.get('computing_memory', None)
computing_partition = request.POST.get('computing_partition', None)
extra_binds = request.POST.get('extra_binds', None)
computing_options = {}
if computing_cpus:
try:
task.computing.manager.start_task(task)
int(computing_cpus)
except:
# Delete the task if could not start it
task.delete()
# ..and re-raise
raise
raise Exception('Cannot convert computing_cpus to int')
computing_options['cpus'] = int(computing_cpus)
# Set step
data['step'] = 'created'
if computing_memory:
computing_options['memory'] = computing_memory
else:
if computing_partition:
computing_options['partition'] = computing_partition
# Set step
data['step'] = 'one'
if computing_options:
task.computing_options = computing_options
# Attach user config to computing
task.computing.attach_user_conf(task.user)
# Set port if not dynamic ports
if not task.container.supports_dynamic_ports:
if task.container.ports:
task.port = task.container.port
# Set extra binds if any:
task.extra_binds = extra_binds
# Save the task before starting it, or the computing manager will not be able to work properly
task.save()
# Start the task
try:
task.computing.manager.start_task(task)
except:
# Delete the task if could not start it
task.delete()
# ..and re-raise
raise
# Set step
data['step'] = 'created'
return render(request, 'create_task.html', {'data': data})
......@@ -608,10 +631,15 @@ def containers(request):
search_text = request.POST.get('search_text', '')
search_type = request.POST.get('search_type', 'All')
# Set bak to page data
# Set back to page data
data['search_type'] = search_type
data['search_text'] = search_text
# Are we using this page as first step of a new task?
data['mode'] = request.GET.get('mode', None)
if not data['mode']:
data['mode'] = request.POST.get('mode', None)
# Do we have to operate on a specific container?
if uuid:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment