From 4f91f56f1ea9a04c3d510b4a25666c0a9420a286 Mon Sep 17 00:00:00 2001 From: Stefano Alberto Russo <stefano.russo@gmail.com> Date: Tue, 5 May 2020 22:09:36 +0200 Subject: [PATCH] UI improvements, few changes in the models as well for the sake of clarity. --- .../webapp/code/rosetta/core_app/admin.py | 4 +- .../rosetta/core_app/computing_managers.py | 50 ++++++--------- .../management/commands/core_app_populate.py | 22 +++---- .../webapp/code/rosetta/core_app/models.py | 41 ++++++++---- .../core_app/static/css/stylish-portfolio.css | 37 ++++++++--- .../js/bootstrap-datetimepicker-4.17.47.js | 14 ++--- .../rosetta/core_app/templates/account.html | 2 +- .../templates/components/computing.html | 32 ++++++++-- .../templates/components/container.html | 49 +++++++++++++-- .../core_app/templates/components/task.html | 62 +++++++++++++++++-- .../core_app/templates/computings.html | 33 +++++----- .../core_app/templates/containers.html | 29 +++++---- .../core_app/templates/create_task.html | 18 +++--- .../templates/edit_computing_conf.html | 1 - .../rosetta/core_app/templates/footer.html | 18 +++++- .../core_app/templates/navigation.html | 10 +-- .../rosetta/core_app/templates/tasks.html | 15 +++-- .../webapp/code/rosetta/core_app/utils.py | 21 +++++++ .../webapp/code/rosetta/core_app/views.py | 28 ++++++--- 19 files changed, 347 insertions(+), 139 deletions(-) diff --git a/services/webapp/code/rosetta/core_app/admin.py b/services/webapp/code/rosetta/core_app/admin.py index 21bc697..56b2fe7 100644 --- a/services/webapp/code/rosetta/core_app/admin.py +++ b/services/webapp/code/rosetta/core_app/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import Profile, LoginToken, Task, Container, Computing, ComputingSysConf, ComputingUserConf, Keys +from .models import Profile, LoginToken, Task, Container, Computing, ComputingSysConf, ComputingUserConf, KeyPair admin.site.register(Profile) admin.site.register(LoginToken) @@ -9,4 +9,4 @@ admin.site.register(Container) admin.site.register(Computing) admin.site.register(ComputingSysConf) admin.site.register(ComputingUserConf) -admin.site.register(Keys) +admin.site.register(KeyPair) diff --git a/services/webapp/code/rosetta/core_app/computing_managers.py b/services/webapp/code/rosetta/core_app/computing_managers.py index 6febb3c..89d2ae1 100644 --- a/services/webapp/code/rosetta/core_app/computing_managers.py +++ b/services/webapp/code/rosetta/core_app/computing_managers.py @@ -1,4 +1,4 @@ -from .models import TaskStatuses, Keys, Task +from .models import TaskStatuses, KeyPair, Task from .utils import os_shell from .exceptions import ErrorMessage, ConsistencyException @@ -164,8 +164,8 @@ class RemoteComputingManager(ComputingManager): user = task.computing.get_conf_param('user') # Get user keys - if task.computing.require_user_keys: - user_keys = Keys.objects.get(user=task.user, default=True) + if task.computing.requires_user_keys: + user_keys = KeyPair.objects.get(user=task.user, default=True) else: raise NotImplementedError('Remote tasks not requiring keys are not yet supported') @@ -173,9 +173,7 @@ class RemoteComputingManager(ComputingManager): from.utils import get_webapp_conn_string webapp_conn_string = get_webapp_conn_string() - - # 1) Run the container on the host (non blocking) - + # Run the container on the host (non blocking) if task.container.type == 'singularity': task.tid = task.uuid @@ -188,17 +186,10 @@ class RemoteComputingManager(ComputingManager): authstring = '' run_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {}@{} '.format(user_keys.private_key_file, user, host) - run_command += '/bin/bash -c \'"wget {}/api/v1/base/agent/?task_uuid={} -O /tmp/agent_{}.py &> /dev/null && export BASE_PORT=\$(python /tmp/agent_{}.py 2> /tmp/{}.log) && '.format(webapp_conn_string, task.uuid, task.uuid, task.uuid, task.uuid) + run_command += '/bin/bash -c \'"wget {}/api/v1/base/agent/?task_uuid={} -O \$HOME/agent_{}.py &> /dev/null && export BASE_PORT=\$(python \$HOME/agent_{}.py 2> \$HOME/{}.log) && '.format(webapp_conn_string, task.uuid, task.uuid, task.uuid, task.uuid) run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\$BASE_PORT && {} '.format(authstring) run_command += 'exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv ' - # ssh -i /rosetta/.ssh/id_rsa -4 -o StrictHostKeyChecking=no slurmclusterworker-one - # "wget 172.21.0.2:8080/api/v1/base/agent/?task_uuid=15a4320a-88b6-4ffc-8dd0-c80f9d18b292 -O /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py &> /dev/null && - # export BASE_PORT=\$(python /tmp/agent_15a4320a-88b6-4ffc-8dd0-c80f9d18b292.py) && export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\$BASE_PORT && export SINGULARITYENV_AUTH_PASS=testpass && - # exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv - # docker://dregistry:5000/rosetta/metadesktop &> /tmp/15a4320a-88b6-4ffc-8dd0-c80f9d18b292.log & echo $!" - - # Set registry if task.container.registry == 'docker_local': # Get local Docker registry conn string @@ -210,7 +201,7 @@ class RemoteComputingManager(ComputingManager): else: raise NotImplementedError('Registry {} not supported'.format(task.container.registry)) - run_command+='{}{} &>> /tmp/{}.log & echo \$!"\''.format(registry, task.container.image, task.uuid) + run_command+='{}{} &>> \$HOME/{}.log & echo \$!"\''.format(registry, task.container.image, task.uuid) else: raise NotImplementedError('Container {} not supported'.format(task.container.type)) @@ -240,8 +231,8 @@ class RemoteComputingManager(ComputingManager): def _stop_task(self, task, **kwargs): # Get user keys - if task.computing.require_user_keys: - user_keys = Keys.objects.get(user=task.user, default=True) + if task.computing.requires_user_keys: + user_keys = KeyPair.objects.get(user=task.user, default=True) else: raise NotImplementedError('Remote tasks not requiring keys are not yet supported') @@ -265,8 +256,8 @@ class RemoteComputingManager(ComputingManager): def _get_task_log(self, task, **kwargs): # Get user keys - if task.computing.require_user_keys: - user_keys = Keys.objects.get(user=task.user, default=True) + if task.computing.requires_user_keys: + user_keys = KeyPair.objects.get(user=task.user, default=True) else: raise NotImplementedError('Remote tasks not requiring keys are not yet supported') @@ -275,7 +266,7 @@ class RemoteComputingManager(ComputingManager): user = task.computing.get_conf_param('user') # Stop the task remotely - view_log_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {}@{} \'/bin/bash -c "cat /tmp/{}.log"\''.format(user_keys.private_key_file, user, host, task.uuid) + view_log_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {}@{} \'/bin/bash -c "cat \$HOME/{}.log"\''.format(user_keys.private_key_file, user, host, task.uuid) out = os_shell(view_log_command, capture=True) if out.exit_code != 0: @@ -290,13 +281,13 @@ class SlurmComputingManager(ComputingManager): def _start_task(self, task, **kwargs): logger.debug('Starting a remote task "{}"'.format(task.computing)) - # Get computing host #Key Error ATM + # Get computing host host = task.computing.get_conf_param('master') user = task.computing.get_conf_param('user') # Get user keys - if task.computing.require_user_keys: - user_keys = Keys.objects.get(user=task.user, default=True) + if task.computing.requires_user_keys: + user_keys = KeyPair.objects.get(user=task.user, default=True) else: raise NotImplementedError('Remote tasks not requiring keys are not yet supported') @@ -323,8 +314,7 @@ class SlurmComputingManager(ComputingManager): # Set output and error files sbatch_args += ' --output=\$HOME/{}.log --error=\$HOME/{}.log '.format(task.uuid, task.uuid) - - # 1) Run the container on the host (non blocking) + # Submit the job if task.container.type == 'singularity': if not task.container.supports_dynamic_ports: @@ -348,7 +338,7 @@ class SlurmComputingManager(ComputingManager): run_command += 'export SINGULARITY_NOHTTPS=true && export SINGULARITYENV_BASE_PORT=\\\\\\$BASE_PORT && {} '.format(authstring) run_command += 'exec nohup singularity run {} --pid --writable-tmpfs --containall --cleanenv '.format(bindings) - # Double to escape for python six for shell (double times three as \\\ escapes a single slash in shell) + # Double to escape for Pythom, six for shell (double times three as \\\ escapes a single slash in shell) # Set registry if task.container.registry == 'docker_local': @@ -399,8 +389,8 @@ class SlurmComputingManager(ComputingManager): def _stop_task(self, task, **kwargs): # Get user keys - if task.computing.require_user_keys: - user_keys = Keys.objects.get(user=task.user, default=True) + if task.computing.requires_user_keys: + user_keys = KeyPair.objects.get(user=task.user, default=True) else: raise NotImplementedError('Remote tasks not requiring keys are not yet supported') @@ -423,8 +413,8 @@ class SlurmComputingManager(ComputingManager): def _get_task_log(self, task, **kwargs): # Get user keys - if task.computing.require_user_keys: - user_keys = Keys.objects.get(user=task.user, default=True) + if task.computing.requires_user_keys: + user_keys = KeyPair.objects.get(user=task.user, default=True) else: raise NotImplementedError('Remote tasks not requiring keys are not yet supported') diff --git a/services/webapp/code/rosetta/core_app/management/commands/core_app_populate.py b/services/webapp/code/rosetta/core_app/management/commands/core_app_populate.py index 9d4e5d5..8f04ffe 100644 --- a/services/webapp/code/rosetta/core_app/management/commands/core_app_populate.py +++ b/services/webapp/code/rosetta/core_app/management/commands/core_app_populate.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand from django.contrib.auth.models import User -from ...models import Profile, Container, Computing, ComputingSysConf, ComputingUserConf, Keys +from ...models import Profile, Container, Computing, ComputingSysConf, ComputingUserConf, KeyPair class Command(BaseCommand): help = 'Adds the admin superuser with \'a\' password.' @@ -34,7 +34,7 @@ class Command(BaseCommand): # Create default keys print('Creating testuser defualt keys') - Keys.objects.create(user = testuser, + KeyPair.objects.create(user = testuser, default = True, private_key_file = '/rosetta/.ssh/id_rsa', public_key_file = '/rosetta/.ssh/id_rsa.pub') @@ -112,9 +112,9 @@ class Command(BaseCommand): Computing.objects.create(user = None, name = 'Local', type = 'local', - require_sys_conf = False, - require_user_conf = False, - require_user_keys = False) + requires_sys_conf = False, + requires_user_conf = False, + requires_user_keys = False) #============================== @@ -123,9 +123,9 @@ class Command(BaseCommand): demo_remote_auth_computing = Computing.objects.create(user = None, name = 'Demo remote', type = 'remote', - require_sys_conf = True, - require_user_conf = True, - require_user_keys = True) + requires_sys_conf = True, + requires_user_conf = True, + requires_user_keys = True) ComputingSysConf.objects.create(computing = demo_remote_auth_computing, data = {'host': 'slurmclusterworker-one'}) @@ -141,9 +141,9 @@ class Command(BaseCommand): demo_slurm_computing = Computing.objects.create(user = None, name = 'Demo Slurm', type = 'slurm', - require_sys_conf = True, - require_user_conf = True, - require_user_keys = True) + requires_sys_conf = True, + requires_user_conf = True, + requires_user_keys = True) # Create demo slurm sys computing conf ComputingSysConf.objects.create(computing = demo_slurm_computing, diff --git a/services/webapp/code/rosetta/core_app/models.py b/services/webapp/code/rosetta/core_app/models.py index 8ee8a36..aa06992 100644 --- a/services/webapp/code/rosetta/core_app/models.py +++ b/services/webapp/code/rosetta/core_app/models.py @@ -3,7 +3,7 @@ from django.conf import settings from django.db import models from django.contrib.auth.models import User from django.utils import timezone -from .utils import os_shell +from .utils import os_shell, color_map, hash_string_to_int if 'sqlite' in settings.DATABASES['default']['ENGINE']: from .fields import JSONField @@ -96,7 +96,11 @@ class Container(models.Model): def id(self): return str(self.uuid).split('-')[0] - + @ property + def color(self): + string_int_hash = hash_string_to_int(self.name + self.type + self.image) + color_map_index = string_int_hash % len(color_map) + return color_map[color_map_index] #========================= # Computing resources @@ -111,16 +115,19 @@ class Computing(models.Model): name = models.CharField('Computing Name', max_length=255, blank=False, null=False) type = models.CharField('Computing Type', max_length=255, blank=False, null=False) - require_sys_conf = models.BooleanField(default=False) - require_user_conf = models.BooleanField(default=False) - require_user_keys = models.BooleanField(default=False) + requires_sys_conf = models.BooleanField(default=False) + requires_user_conf = models.BooleanField(default=False) + requires_user_keys = models.BooleanField(default=False) + + supports_docker = models.BooleanField(default=False) + supports_singularity = models.BooleanField(default=False) def __str__(self): if self.user: - return str('Computing Resource "{}" of user "{}"'.format(self.name, self.user)) + return str('Computing "{}" of user "{}"'.format(self.name, self.user)) else: - return str('Computing Resource "{}"'.format(self.name)) + return str('Computing "{}"'.format(self.name)) @property @@ -172,6 +179,12 @@ class Computing(models.Model): ComputingManager = getattr(computing_managers, '{}ComputingManager'.format(self.type.title())) return ComputingManager() + @ property + def color(self): + string_int_hash = hash_string_to_int(self.name) + color_map_index = string_int_hash % len(color_map) + return color_map[color_map_index] + class ComputingSysConf(models.Model): @@ -201,7 +214,7 @@ class ComputingUserConf(models.Model): @property def id(self): - return str('Computing sys conf for {} with id "{}" of user "{}"'.format(self.computing, self.id, self.user)) + return str('Computing user conf for {} with id "{}" of user "{}"'.format(self.computing, self.id, self.user)) @@ -274,13 +287,19 @@ class Task(models.Model): def __str__(self): return str('Task "{}" of user "{}" running on "{}" in status "{}" created at "{}"'.format(self.name, self.user, self.computing, self.status, self.created)) + @ property + def color(self): + string_int_hash = hash_string_to_int(self.name) + color_map_index = string_int_hash % len(color_map) + return color_map[color_map_index] + #========================= -# Keys +# KeyPair #========================= -class Keys(models.Model): +class KeyPair(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, related_name='+', on_delete=models.CASCADE, null=False) @@ -292,7 +311,7 @@ class Keys(models.Model): def __str__(self): - return str('Keys with id "{}" of user "{}"'.format(self.id, self.user)) + return str('KeyPair with id "{}" of user "{}"'.format(self.id, self.user)) @property diff --git a/services/webapp/code/rosetta/core_app/static/css/stylish-portfolio.css b/services/webapp/code/rosetta/core_app/static/css/stylish-portfolio.css index 4c5823f..ce18de9 100644 --- a/services/webapp/code/rosetta/core_app/static/css/stylish-portfolio.css +++ b/services/webapp/code/rosetta/core_app/static/css/stylish-portfolio.css @@ -13,9 +13,12 @@ body { font-size: 1.0em; } -/*body { - font-family: "Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif; -}*/ +body { + /*font-family: "Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;*/ + /*color: #585858*/ + color: #484848 + +} .form-control { @@ -54,12 +57,17 @@ body { /* Custom Button Styles */ -.btn-dark { +.btn-dark, .btn-dark-disabled { border-radius: 0; color: #fff; background-color: rgba(0,0,0,0.4); } +.btn-disabled { + border-radius: 0; + color: #f5f5f5; + background-color: #c8c8c8; +} .btn-blue { border-radius: 0; @@ -70,8 +78,24 @@ body { } +.btn-connect { + border-radius: 0; + color: #fff; + background-color: rgba(0,95,144,0.8); +} + +.btn-connect:hover { + color: #fff; + background-color: rgba(0,95,144,1.0); +} +.btn-disabled:hover { + color: #f5f5f5; +} +.btn-dark-disabled:hover { + color: #fff; +} .btn-dark:hover, .btn-dark:focus, @@ -431,9 +455,6 @@ th, td { font-weight:700; } - - - .centerbox-error-notfilled { background-color: #ffffff; text-align: center; @@ -522,7 +543,7 @@ th, td { /* Dashboard */ /*-------------------*/ .dashboard table { - /*border: 1px #c0c0c0 solid;*/ + border: 1px #e0e0e0 solid; background: #F8F8F8; padding: 10px; } diff --git a/services/webapp/code/rosetta/core_app/static/js/bootstrap-datetimepicker-4.17.47.js b/services/webapp/code/rosetta/core_app/static/js/bootstrap-datetimepicker-4.17.47.js index 62c1d30..e5f7735 100755 --- a/services/webapp/code/rosetta/core_app/static/js/bootstrap-datetimepicker-4.17.47.js +++ b/services/webapp/code/rosetta/core_app/static/js/bootstrap-datetimepicker-4.17.47.js @@ -1274,10 +1274,10 @@ var handler = null, index, index2, - pressedKeys = [], + pressedKeyPair = [], pressedModifiers = {}, currentKey = e.which, - keyBindKeys, + keyBindKeyPair, allModifiersPressed, pressed = 'p'; @@ -1285,7 +1285,7 @@ for (index in keyState) { if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { - pressedKeys.push(index); + pressedKeyPair.push(index); if (parseInt(index, 10) !== currentKey) { pressedModifiers[index] = true; } @@ -1294,11 +1294,11 @@ for (index in options.keyBinds) { if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { - keyBindKeys = index.split(' '); - if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { + keyBindKeyPair = index.split(' '); + if (keyBindKeyPair.length === pressedKeyPair.length && keyMap[currentKey] === keyBindKeyPair[keyBindKeyPair.length - 1]) { allModifiersPressed = true; - for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { - if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { + for (index2 = keyBindKeyPair.length - 2; index2 >= 0; index2--) { + if (!(keyMap[keyBindKeyPair[index2]] in pressedModifiers)) { allModifiersPressed = false; break; } diff --git a/services/webapp/code/rosetta/core_app/templates/account.html b/services/webapp/code/rosetta/core_app/templates/account.html index 865dbac..1e30829 100644 --- a/services/webapp/code/rosetta/core_app/templates/account.html +++ b/services/webapp/code/rosetta/core_app/templates/account.html @@ -86,7 +86,7 @@ </table> <br /> - <h3>Keys</h3> + <h3>KeyPair</h3> <table class="dashboard"> <tr> diff --git a/services/webapp/code/rosetta/core_app/templates/components/computing.html b/services/webapp/code/rosetta/core_app/templates/components/computing.html index 38b624e..8945613 100644 --- a/services/webapp/code/rosetta/core_app/templates/components/computing.html +++ b/services/webapp/code/rosetta/core_app/templates/components/computing.html @@ -1,8 +1,10 @@ - <table class="dashboard" style="max-width:500px"> + + <div style="float:left; width:500px; margin:10px; margin-bottom:20px"> + <table class="dashboard" style="width:100%"> <tr> - <td><b>ID</b></td> - <td><a href="?uuid={{ computing.uuid }}">{{ computing.id }}</a></td> + <td colspan="2" style="background:{{ computing.color }}; height:30px"> + </td> </tr> <tr> @@ -10,6 +12,11 @@ <td>{{ computing.name }}</td> </tr> + <tr> + <td width="110px"><b>ID</b></td> + <td>{{ computing.id }}</a></td> + </tr> + <tr> <td><b>Type</b></td> <td>{{ computing.type }}</td> @@ -20,6 +27,23 @@ <td>{% if computing.user %}{{ computing.user }}{% else %}platform{% endif %}</td> </tr> + <tr> + <td><b>Require</b></td> + <td> + Sys conf: {{ computing.requires_sys_conf }} <br/> + User conf: {{ computing.requires_user_conf }} <br/> + User keys: {{ computing.requires_user_keys }} + </td> + </tr> + + <tr> + <td><b>Supports</b></td> + <td> + Docker: {{ computing.supports_docker }} <br/> + Singularity: {{ computing.supports_singularity }} + </td> + </tr> + <tr> <td><b>Sys Conf</b></td> <td>{{ computing.sys_conf_data_json }} {% if request.user.is_superuser %} [<a href="/edit_computing_conf?type=sys&computing_uuid={{ computing.uuid}}">Edit</a>] {% endif %}</td> @@ -38,4 +62,4 @@ </tr> {% endif %} </table> - <br/> \ No newline at end of file + </div> \ No newline at end of file diff --git a/services/webapp/code/rosetta/core_app/templates/components/container.html b/services/webapp/code/rosetta/core_app/templates/components/container.html index 072d1a4..352a5de 100644 --- a/services/webapp/code/rosetta/core_app/templates/components/container.html +++ b/services/webapp/code/rosetta/core_app/templates/components/container.html @@ -1,8 +1,12 @@ - <table class="dashboard"> + + {% if details %} + + <table class="dashboard" style="margin:10px"> + <tr> - <td><b>ID</b></td> - <td><a href="?uuid={{ container.uuid }}">{{ container.id }}</a></td> + <td colspan="2" style="background:{{ container.color }}; height:25px"> + </td> </tr> <tr> @@ -10,6 +14,11 @@ <td>{{ container.name }}</td> </tr> + <tr> + <td><b>ID</b></td> + <td>{{ container.id }}</td> + </tr> + <tr> <td><b>Image</b></td> <td>{{ container.image }}</td> @@ -74,4 +83,36 @@ </tr> {% endif %} </table> - <br/> \ No newline at end of file + <br/> + + + {% else %} + <div style="width:300px; float:left; border: #e0e0e0 solid 1px; margin:10px; background:#f8f8f8; margin-bottom:20px"> + <div style="background:{{container.color}}; height:24px"></div> + + <div style="padding:10px; text-align:center; border-bottom: #e0e0e0 solid 1px; "> + <a href="?uuid={{ container.uuid }}">{{ container.name }}</a> + <a href="/create_task?container_uuid={{ container.uuid }}"><i class="fa fa-play" style="color:green"></i></a> + </div> + + <div style="padding:10px;"> + Type: {{ container.type }}<br/> + Image: {{ container.image }} + </div> + + <div style="margin-bottom:10px; text-align:center"> + + </div> + + + + </div> + + {% endif %} + + + + + + + \ No newline at end of file diff --git a/services/webapp/code/rosetta/core_app/templates/components/task.html b/services/webapp/code/rosetta/core_app/templates/components/task.html index 4756d59..c0920e4 100644 --- a/services/webapp/code/rosetta/core_app/templates/components/task.html +++ b/services/webapp/code/rosetta/core_app/templates/components/task.html @@ -1,14 +1,18 @@ - + + + {% if details %} + + <table class="dashboard"> <tr> - <td><b>ID</b></td> - <td><a href="?uuid={{ task.uuid }}">{{ task.id }}</a></td> + <td><b>Name</b></td> + <td>{{ task.name }}</td> </tr> <tr> - <td><b>Name</b></td> - <td>{{ task.name }}</td> + <td><b>ID</b></td> + <td>{{ task.id }}</td> </tr> <tr> @@ -85,4 +89,50 @@ </tr> </table> - <br/> \ No newline at end of file + + {% else %} + <div style="width:300px; float:left; border: #e0e0e0 solid 1px; margin:10px; background:#f8f8f8; margin-bottom:20px"> + <!-- <div> + <div style="background:{{task.container.color}}; height:24px; width:149px; float:left"></div> + <div style="background:{{task.computing.color}}; height:24px; width:149px; float:left"></div> + </div> --> + + <!-- <div style="background:{{task.color}}; height:24px;"></div> --> + + <div style="padding:10px; text-align:center; border-bottom: #e0e0e0 solid 1px; "> + <a href="?uuid={{ task.uuid }}"><b>{{ task.name }}</b></a> + </div> + + <div style="padding:10px;"> + <!-- <b>Container:</b> {{ task.container.name }} <span style="font-size:14px; background-color:{{task.container.color}}"> </span> <br/> + <b>Computing:</b> {{ task.computing.name }} <span style="font-size:14px; background-color:{{task.computing.color}}"> </span><br/> --> + + <b>Container:</b> {{ task.container.name }} + <a href="/containers/?uuid={{ task.container.uuid }}" style="color:{{task.container.color}}"><i class="fa fa-external-link" ></i></a> <br/> + + <b>Computing:</b> {{ task.computing.name }} + <a href="/computings/" style="color:{{task.computing.color}}"><i class="fa fa-external-link" ></i></a><br/> + + + {% if task.status == "running" %} + <b>Status:</b> <font color="green">running</font> + {% else %} + <b>Status:</b> {{ task.status }} + {% endif %} + </div> + + <div style="margin-bottom:10px; text-align:center; padding:5px"> + {% if task.status == "running" %} + <a href="?uuid={{task.uuid}}&action=connect" class="btn btn-connect">Connect</a> + {% else %} + <a href="?uuid={{task.uuid}}&action=connect" class="btn btn-disabled">Connect</a> + {% endif %} + + + </div> + + + + </div> + + {% endif %} \ No newline at end of file diff --git a/services/webapp/code/rosetta/core_app/templates/computings.html b/services/webapp/code/rosetta/core_app/templates/computings.html index 56271a1..efaa389 100644 --- a/services/webapp/code/rosetta/core_app/templates/computings.html +++ b/services/webapp/code/rosetta/core_app/templates/computings.html @@ -10,31 +10,32 @@ <div class="span8 offset2"> {% if data.computing %} - <h1><a href="/computings">Computing List</a> > {{ data.computing.id }} </h1> + <h1><a href="/computings">Computing</a> > {{ data.computing.id }} </h1> {% else %} - <h1>Computing List</h1> + <h1>Computing</h1> {% endif %} <hr/> - + <div class="row" style="padding:5px"> {% if data.computing %} - {% include "components/computing.html" with computing=data.computing %} - {% else %} - + {% include "components/computing.html" with computing=data.computing details=True %} + {% else %} {% for computing in data.computings %} {% include "components/computing.html" with computing=computing %} - <br /> {% endfor %} - - - <!-- <br /> - <a href="/add_computing">Add new...</a> --> - {% endif %} - <br /> - <br /> - <br /> - <br /> + </div> + + <!-- <div class="row" style="padding:10px; padding-left:15px"> + <a href="/add_container">Add new...</a> + </div> --> + + <br/> + <br/> + <br/> + <br/> + <br/> + <br/> </div> </div> diff --git a/services/webapp/code/rosetta/core_app/templates/containers.html b/services/webapp/code/rosetta/core_app/templates/containers.html index 2c461e8..5ed6480 100644 --- a/services/webapp/code/rosetta/core_app/templates/containers.html +++ b/services/webapp/code/rosetta/core_app/templates/containers.html @@ -10,29 +10,32 @@ <div class="span8 offset2"> {% if data.container %} - <h1><a href="/containers">Container List</a> > {{ data.container.id }} </h1> + <h1><a href="/containers">Containers</a> <span style="font-size:18px"> / {{ data.container.name }}</span></h1> {% else %} - <h1>Container List</h1> + <h1>Containers</h1> {% endif %} <hr/> - + <div class="row" style="padding:5px"> {% if data.container %} - {% include "components/container.html" with container=data.container %} + {% include "components/container.html" with container=data.container details=True %} {% else %} {% for container in data.containers %} {% include "components/container.html" with container=container %} - <br /> {% endfor %} - - <br /> - <a href="/add_container">Add new...</a> - {% endif %} - <br /> - <br /> - <br /> - <br /> + </div> + + <div class="row" style="padding:10px; padding-left:15px"> + <a href="/add_container">Add new...</a> + </div> + + <br/> + <br/> + <br/> + <br/> + <br/> + <br/> </div> </div> diff --git a/services/webapp/code/rosetta/core_app/templates/create_task.html b/services/webapp/code/rosetta/core_app/templates/create_task.html index 3e3ca9d..84545a0 100644 --- a/services/webapp/code/rosetta/core_app/templates/create_task.html +++ b/services/webapp/code/rosetta/core_app/templates/create_task.html @@ -34,16 +34,18 @@ <tr> <td><b>Task container</b></td><td> + {% if data.container %} + <select name="task_container_uuid"> + <option value="{{data.container.uuid}}" selected>{{data.container.name}} ({{data.container.type}})</option> + </select> + {% else %} <select name="task_container_uuid" > - <!-- <option value="metadesktop" selected>Meta Desktop</option> - <option value="astroccok">Astrocook</option> - <option value="gadgetviewer">Gadget Viewer</option> --> {% for container in data.containers %} <option value="{{container.uuid}}">{{container.name}} ({{container.type}})</option> - {% endfor %} - + {% endfor %} </select> - | <a href="/add_container">Add new...</a> + {% endif %} + <!-- | <a href="/add_container">Add new...</a> --> </td> </tr> @@ -51,10 +53,10 @@ <td><b>Computing resource</b></td><td> <select name="task_computing" > {% for computing in data.computings %}} - <option value="{{ computing.uuid }}">{{ computing.name}} ({% if computing.user %}{{ computing.user }}{% else %}platform{% endif %})</option> + <option value="{{ computing.uuid }}">{{ computing.name}}</option> <!-- ({% if computing.user %}{{ computing.user }}{% else %}platform{% endif %}) --> {% endfor %} </select> - | <a href="/add_computing">Add new...</a> + <!-- | <a href="/add_computing">Add new...</a>--> </td> </tr> diff --git a/services/webapp/code/rosetta/core_app/templates/edit_computing_conf.html b/services/webapp/code/rosetta/core_app/templates/edit_computing_conf.html index efcbca3..82092ba 100644 --- a/services/webapp/code/rosetta/core_app/templates/edit_computing_conf.html +++ b/services/webapp/code/rosetta/core_app/templates/edit_computing_conf.html @@ -11,7 +11,6 @@ <h1>Edit computing conf</h1> <hr> - <h4>Edit the configuration in JSON format for {{ data.computing }}</h4> <br/> diff --git a/services/webapp/code/rosetta/core_app/templates/footer.html b/services/webapp/code/rosetta/core_app/templates/footer.html index f7c760f..6e4daa4 100644 --- a/services/webapp/code/rosetta/core_app/templates/footer.html +++ b/services/webapp/code/rosetta/core_app/templates/footer.html @@ -31,8 +31,24 @@ } }); }); - </script> + function stringToColour(str) { + + // str to hash + for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash)); + + // int/hash to hex + for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2)); + + colour_rgb = hexToRgb(colour) + + return "rgba(" + colour_rgb.r + "," + colour_rgb.g + "," + colour_rgb.b + ",0.3)" + + } + + </script> + + </body> </html> diff --git a/services/webapp/code/rosetta/core_app/templates/navigation.html b/services/webapp/code/rosetta/core_app/templates/navigation.html index b18e4f6..0693114 100644 --- a/services/webapp/code/rosetta/core_app/templates/navigation.html +++ b/services/webapp/code/rosetta/core_app/templates/navigation.html @@ -25,14 +25,16 @@ <a href="/account" onclick = $("#menu-close").click(); >Account</a> </li> <li> - <a href="/tasks" onclick = $("#menu-close").click(); >Tasks</a> + <a href="/containers" onclick = $("#menu-close").click(); >Containers</a> </li> <li> <a href="/computings" onclick = $("#menu-close").click(); >Computing</a> - </li> + </li> <li> - <a href="/containers" onclick = $("#menu-close").click(); >Containers</a> - </li> + <a href="/tasks" onclick = $("#menu-close").click(); >Tasks</a> + </li> + + {% else %} <li> <center> diff --git a/services/webapp/code/rosetta/core_app/templates/tasks.html b/services/webapp/code/rosetta/core_app/templates/tasks.html index 46e808c..cf14bdc 100644 --- a/services/webapp/code/rosetta/core_app/templates/tasks.html +++ b/services/webapp/code/rosetta/core_app/templates/tasks.html @@ -9,24 +9,29 @@ <div class="dashboard"> <div class="span8 offset2"> - {% if data.task %} - <h1><a href="/tasks">Task List</a> > {{ data.task.id }} </h1> + <h1><a href="/tasks">Tasks</a> <span style="font-size:18px"> / {{ data.task.name }}</span></h1> {% else %} - <h1>Task List</h1> + <h1>Tasks</h1> {% endif %} <hr> + <div class="row" style="padding:5px"> {% if data.task %} {% include "components/task.html" with task=data.task details=True %} {% else %} {% for task in data.tasks %} {% include "components/task.html" with task=task %} {% endfor %} - <br /> + {% endif %} + </div> + + {% if not data.task %} + <div class="row" style="padding:10px; padding-left:15px"> <a href="/create_task">Create new...</a> + </div> {% endif %} - + <br/> <br/> <br/> diff --git a/services/webapp/code/rosetta/core_app/utils.py b/services/webapp/code/rosetta/core_app/utils.py index 6d1bfb8..2cb1455 100644 --- a/services/webapp/code/rosetta/core_app/utils.py +++ b/services/webapp/code/rosetta/core_app/utils.py @@ -1,4 +1,5 @@ import os +import hashlib import traceback import hashlib import random @@ -14,6 +15,20 @@ from .exceptions import ErrorMessage logger = logging.getLogger(__name__) +# Colormap (See https://bhaskarvk.github.io/colormap/reference/colormap.html) +color_map = ["#440154", "#440558", "#450a5c", "#450e60", "#451465", "#461969", + "#461d6d", "#462372", "#472775", "#472c7a", "#46307c", "#45337d", + "#433880", "#423c81", "#404184", "#3f4686", "#3d4a88", "#3c4f8a", + "#3b518b", "#39558b", "#37598c", "#365c8c", "#34608c", "#33638d", + "#31678d", "#2f6b8d", "#2d6e8e", "#2c718e", "#2b748e", "#29788e", + "#287c8e", "#277f8e", "#25848d", "#24878d", "#238b8d", "#218f8d", + "#21918d", "#22958b", "#23988a", "#239b89", "#249f87", "#25a186", + "#25a584", "#26a883", "#27ab82", "#29ae80", "#2eb17d", "#35b479", + "#3cb875", "#42bb72", "#49be6e", "#4ec16b", "#55c467", "#5cc863", + "#61c960", "#6bcc5a", "#72ce55", "#7cd04f", "#85d349", "#8dd544", + "#97d73e", "#9ed93a", "#a8db34", "#b0dd31", "#b8de30", "#c3df2e", + "#cbe02d", "#d6e22b", "#e1e329", "#eae428", "#f5e626", "#fde725"] + #====================== # Utility functions #====================== @@ -465,6 +480,12 @@ def get_tunnel_host(): tunnel_host = os.environ.get('ROSETTA_TUNNEL_HOST', 'localhost') return tunnel_host +def hash_string_to_int(string): + #int_hash = 0 + #for char in string: + # int_hash += ord(char) + #return int_hash + return int(hashlib.sha1(string.encode('utf8')).hexdigest(), 16) #% (10 ** 8) diff --git a/services/webapp/code/rosetta/core_app/views.py b/services/webapp/code/rosetta/core_app/views.py index db53296..07b87b6 100644 --- a/services/webapp/code/rosetta/core_app/views.py +++ b/services/webapp/code/rosetta/core_app/views.py @@ -8,7 +8,7 @@ from django.contrib.auth import authenticate, login, logout from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth.models import User from django.shortcuts import redirect -from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, Keys, ComputingSysConf, ComputingUserConf +from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, KeyPair, ComputingSysConf, ComputingUserConf from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username from .decorators import public_view, private_view from .exceptions import ErrorMessage @@ -186,7 +186,7 @@ def register_view(request): # Create key objects - Keys.objects.create(user = user, + KeyPair.objects.create(user = user, default = True, private_key_file = '/data/resources/keys/{}_id_rsa'.format(user.username), public_key_file = '/data/resources/keys/{}_id_rsa.pub'.format(user.username)) @@ -250,7 +250,7 @@ def account(request): edit = None # Set data.default_public_key - with open(Keys.objects.get(user=request.user, default=True).public_key_file) as f: + with open(KeyPair.objects.get(user=request.user, default=True).public_key_file) as f: data['default_public_key'] = f.read() # Edit values @@ -456,13 +456,25 @@ def create_task(request): data['profile'] = Profile.objects.get(user=request.user) data['title'] = 'New Task' - # Get containers and computings - data['containers'] = list(Container.objects.filter(user=None)) + list(Container.objects.filter(user=request.user)) - data['computings'] = list(Computing.objects.filter(user=None)) + list(Computing.objects.filter(user=request.user)) - # Step if any step = request.POST.get('step', None) + # Container uuid if any + container_uuid = request.GET.get('container_uuid', None) + if container_uuid: + try: + data['container'] = Container.objects.get(uuid=container_uuid, user=request.user) + except Container.DoesNotExist: + data['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)) + + + if step == 'one': # We have a step one submitted, get the first tab parameters @@ -857,6 +869,8 @@ def edit_computing_conf(request): computingSysConf.data = new_conf_data computingSysConf.save() data['saved'] = True + return HttpResponseRedirect('/computings') + # Dump conf data for the webpage if computingSysConf.data: -- GitLab