Skip to content
Snippets Groups Projects
Commit e2e0e419 authored by Robbie Luijben's avatar Robbie Luijben
Browse files

LDVSpec usability improvements

parent 3b91670b
No related branches found
No related tags found
1 merge request!103LDVSpec usability improvements
Showing
with 114 additions and 30 deletions
...@@ -69,6 +69,12 @@ integration-test: ...@@ -69,6 +69,12 @@ integration-test:
- docker compose build - docker compose build
- docker compose up -d atdb-backend ldv-specification-backend - docker compose up -d atdb-backend ldv-specification-backend
- docker compose run integration - docker compose run integration
artifacts:
paths:
- /workdir/output.xml
- /workdir/log.html
- /workdir/report.html
expire_in: 1 week
allow_failure: false allow_failure: false
rules: rules:
- when: on_success - when: on_success
......
...@@ -56,3 +56,35 @@ Test create a group with a few work specifications ...@@ -56,3 +56,35 @@ Test create a group with a few work specifications
Add Group Beatles 1, 22, 13 3 Add Group Beatles 1, 22, 13 3
Check if Group Exists Beatles Check if Group Exists Beatles
Test task submission status ready with files
[Tags] Workspecification
${specification_id}= Add Work Specification 5
${submission_status}= Get Task Submission Status ${specification_id}
Should Be Equal As Strings ${submission_status} Ready
Test task submission status ready without files
[Tags] Workspecification
${specification_id}= Add Work Specification 9999
${submission_status}= Get Task Submission Status ${specification_id}
Should Be Equal As Strings ${submission_status} Ready*
Test task submission status submitted
[Tags] Workspecification
${specification_id}= Add Work Specification 5
Submit Work Specification ${specification_id}
${submission_status}= Get Task Submission Status ${specification_id}
Should Be Equal As Strings ${submission_status} Submitted
# This test depends on one ATDB site added in the suite setup
Test automatically select first processing site dropdown in workspecification create form
[Tags] Workspecification
Go To ${LDVSPEC}specification/add/
${selected_value}= Get Selected List Value //*[@data-test-id="processing_site"]
Should Be Equal As Strings ${selected_value} ${ATDB_SITE_NAME}
# This test depends on one ATDB site added in the suite setup
Test automatically select first processing site dropdown in group create form
[Tags] Workspecification
Go To ${LDVSPEC}group/add/
${selected_value}= Get Selected List Value //*[@data-test-id="processing_site"]
Should Be Equal As Strings ${selected_value} ${ATDB_SITE_NAME}
\ No newline at end of file
...@@ -13,7 +13,7 @@ Add Group ...@@ -13,7 +13,7 @@ Add Group
Input Text //*[@data-test-id="group_name"] ${name} Input Text //*[@data-test-id="group_name"] ${name}
Select From List By Value //*[@data-test-id="processing_site"] atdb_site Select From List By Value //*[@data-test-id="processing_site"] ${ATDB_SITE_NAME}
Wait Until Element Contains //*[@data-test-id="workflow_tag"] environment timeout=5s Wait Until Element Contains //*[@data-test-id="workflow_tag"] environment timeout=5s
Select From List By Value //*[@data-test-id="workflow_tag"] environment Select From List By Value //*[@data-test-id="workflow_tag"] environment
Wait Until Element Contains //*[@data-test-id="workflow"] https://example.com/workflow-1 timeout=5s Wait Until Element Contains //*[@data-test-id="workflow"] https://example.com/workflow-1 timeout=5s
...@@ -42,7 +42,7 @@ Add Work Specification ...@@ -42,7 +42,7 @@ Add Work Specification
[Arguments] ${obs_id} [Arguments] ${obs_id}
Go To ${LDVSPEC}specification/add/ Go To ${LDVSPEC}specification/add/
Click Element //*[@data-test-id="processing_site"] Click Element //*[@data-test-id="processing_site"]
Select From List By Value //*[@data-test-id="processing_site"] atdb_site Select From List By Value //*[@data-test-id="processing_site"] ${ATDB_SITE_NAME}
Wait Until Element Contains //*[@data-test-id="workflow_tag"] environment timeout=5s Wait Until Element Contains //*[@data-test-id="workflow_tag"] environment timeout=5s
Select From List By Value //*[@data-test-id="workflow_tag"] environment Select From List By Value //*[@data-test-id="workflow_tag"] environment
Wait Until Element Contains //*[@data-test-id="workflow"] https://example.com/workflow-1 timeout=5s Wait Until Element Contains //*[@data-test-id="workflow"] https://example.com/workflow-1 timeout=5s
...@@ -93,7 +93,7 @@ Add LDV Data Product Filter ...@@ -93,7 +93,7 @@ Add LDV Data Product Filter
Add ATDB Processing Site Add ATDB Processing Site
[Arguments] ${token} [Arguments] ${token}
Go To ${LDVSPEC}admin/lofardata/atdbprocessingsite/add/ Go To ${LDVSPEC}admin/lofardata/atdbprocessingsite/add/
Input Text id_name atdb_site Input Text id_name ${ATDB_SITE_NAME}
Input Text id_url ${ATDB} Input Text id_url ${ATDB}
Input Text id_access_token ${token} Input Text id_access_token ${token}
Click Button _save Click Button _save
...@@ -109,6 +109,12 @@ Logout from LDV Spec ...@@ -109,6 +109,12 @@ Logout from LDV Spec
Go To ${LDVSPEC}accounts/logout/ Go To ${LDVSPEC}accounts/logout/
Click Element //button[contains(text(), 'Log out')] Click Element //button[contains(text(), 'Log out')]
Get Task Submission Status
[Arguments] ${specification_id}
Go To ${LDVSPEC}specification/${specification_id}
${submission_status}= Get Text //*[@data-test-id="submission-status"]
RETURN ${submission_status}
# ATDB # ATDB
Create Example Workflow in ATDB Create Example Workflow in ATDB
......
...@@ -3,3 +3,4 @@ ${BROWSER} headlessfirefox ...@@ -3,3 +3,4 @@ ${BROWSER} headlessfirefox
${LDVSPEC} http://ldv-specification-backend:8000/ldvspec/ ${LDVSPEC} http://ldv-specification-backend:8000/ldvspec/
${LDVSPEC_API} http://ldv-specification-backend:8000/ldvspec/api/ ${LDVSPEC_API} http://ldv-specification-backend:8000/ldvspec/api/
${ATDB} http://atdb.backend:8000/atdb/ ${ATDB} http://atdb.backend:8000/atdb/
${ATDB_SITE_NAME} atdb_site
\ No newline at end of file
...@@ -124,6 +124,9 @@ class Group(models.Model): ...@@ -124,6 +124,9 @@ class Group(models.Model):
selected_workflow = models.CharField(max_length=500, blank=True, default='') selected_workflow = models.CharField(max_length=500, blank=True, default='')
selected_workflow_tag = models.CharField(max_length=500, blank=True, default='') selected_workflow_tag = models.CharField(max_length=500, blank=True, default='')
def ordered_workspecifications(self):
return self.workspecification_set.order_by('id')
class WorkSpecification(models.Model): class WorkSpecification(models.Model):
"""Work Specification""" """Work Specification"""
...@@ -139,7 +142,7 @@ class WorkSpecification(models.Model): ...@@ -139,7 +142,7 @@ class WorkSpecification(models.Model):
# ATDB Workflow URL # ATDB Workflow URL
selected_workflow = models.CharField(max_length=500, null=True) selected_workflow = models.CharField(max_length=500, null=True)
selected_workflow_tag = models.CharField(max_length=500, null=True, default="Unknown") selected_workflow_tag = models.CharField(max_length=500, null=True)
# Task ID's that were created in ATDB # Task ID's that were created in ATDB
atdb_task_ids = ArrayField(models.IntegerField(), null=True, blank=True) atdb_task_ids = ArrayField(models.IntegerField(), null=True, blank=True)
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
processingSites processingSites
); );
self.selectedProcessingSite = ko.observable(existingProcessingSite); self.selectedProcessingSite = ko.observable(initialProcessingSite);
self.selectedProcessingSite.subscribe((context) => { self.selectedProcessingSite.subscribe((context) => {
self.loadWorkflows(); self.loadWorkflows();
...@@ -26,9 +26,12 @@ ...@@ -26,9 +26,12 @@
self.selectedGroup = ko.observable(existingGroup); self.selectedGroup = ko.observable(existingGroup);
self.groupHasBeenSelectedAtLeastOnce = false;
self.selectedGroup.subscribe((context) => { self.selectedGroup.subscribe((context) => {
const group = self.groups() && self.groups().filter(group => group.id === self.selectedGroup())[0]; const group = self.groups() && self.groups().filter(group => group.id === self.selectedGroup())[0];
if (group) { if (group) {
self.groupHasBeenSelectedAtLeastOnce = true;
self.selectedProcessingSite(group.processing_site); self.selectedProcessingSite(group.processing_site);
self.selectedTag(group.selected_workflow_tag); self.selectedTag(group.selected_workflow_tag);
self.selectedWorkflow(group.selected_workflow); self.selectedWorkflow(group.selected_workflow);
...@@ -36,7 +39,10 @@ ...@@ -36,7 +39,10 @@
// setting the processing site to null will cause the loadWorkflows // setting the processing site to null will cause the loadWorkflows
// function to trigger, which in turn will clear the previously selected // function to trigger, which in turn will clear the previously selected
// filled in workflow and tag // filled in workflow and tag
if (!self.pk) { // only do this for new work specifications (not existing ones) if (!self.pk && self.groupHasBeenSelectedAtLeastOnce) {
// only do this for new work specifications (not existing ones)
// additionally, only do this when a group has already been selected once
// this is to avoid clearing the auto selected first processing site option
self.selectedProcessingSite(null); self.selectedProcessingSite(null);
} }
} }
...@@ -62,8 +68,13 @@ ...@@ -62,8 +68,13 @@
self.selectedTag(group.selected_workflow_tag); self.selectedTag(group.selected_workflow_tag);
self.selectedWorkflow(group.selected_workflow); self.selectedWorkflow(group.selected_workflow);
} else { } else {
self.selectedTag(self.existing_workflow_tag); // auto select first tag if there is only 1, and there is no tag saved on the work specification yet
self.selectedWorkflow(self.existing_workflow); const autoSelectedSingleTag = self.tags().length === 1 ? self.tags()[0] : null;
self.selectedTag(self.existing_workflow_tag || autoSelectedSingleTag);
// auto select first workflow if there is only 1, and there is no workflow saved on the work specification yet
const autoSelectedWorkflow = self.workflowsByTag().length === 1 ? self.workflowsByTag()[0].workflow_uri : null;
self.selectedWorkflow(self.existing_workflow || autoSelectedWorkflow);
} }
}).finally(() => { }).finally(() => {
self.isLoading(false); self.isLoading(false);
......
...@@ -12,10 +12,14 @@ ...@@ -12,10 +12,14 @@
<a class="icon icon--times button--close" href="{% url 'index' %}"></a> <a class="icon icon--times button--close" href="{% url 'index' %}"></a>
<header class="flex-wrapper flex-wrapper--centered flex-wrapper--column"> <header class="flex-wrapper flex-wrapper--centered flex-wrapper--column">
{% if object.pk %} {% if object.pk %}
<h2 class="title text text--primary">Edit {{ object.name }}</h2> <h2 class="title text">Edit {{ object.name }}
{% include 'lofardata/partial_templates/user_guide_link.html' with link="group/editing" %}
</h2>
<form method="post" action="{% url 'group-update' object.pk %}"> <form method="post" action="{% url 'group-update' object.pk %}">
{% else %} {% else %}
<h2 class="title text text--primary">New Group </h2> <h2 class="title text">New Group
{% include 'lofardata/partial_templates/user_guide_link.html' with link="group/creating" %}
</h2>
<form method="post" action="{% url 'group-create' %}"> <form method="post" action="{% url 'group-create' %}">
{% endif %} {% endif %}
...@@ -173,7 +177,7 @@ ...@@ -173,7 +177,7 @@
const existingWorkflow = '{{ object.selected_workflow|default_if_none:"null" }}'; const existingWorkflow = '{{ object.selected_workflow|default_if_none:"null" }}';
const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"null" }}'; const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"null" }}';
const processingSites = {{ processing_sites | json }}; const processingSites = {{ processing_sites | json }};
const existingProcessingSite = '{{ object.processing_site.name }}'; const initialProcessingSite = '{{ object.processing_site.name|default:single_processing_site }}';
const groups = null; const groups = null;
const existingGroup = null; const existingGroup = null;
const pk = {{ object.pk|default_if_none:0 }}; const pk = {{ object.pk|default_if_none:0 }};
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
<div class="table"> <div class="table">
{% include 'lofardata/partial_templates/table_header.html' with id=group.name %} {% include 'lofardata/partial_templates/table_header.html' with id=group.name %}
<div class="table__content table__content--scroll table__content--scroll-small"> <div class="table__content table__content--scroll table__content--scroll-small">
{% for specification in group.workspecification_set.all %} {% for specification in group.ordered_workspecifications %}
{% include 'lofardata/partial_templates/table_entry.html' with specification=specification id=group.name %} {% include 'lofardata/partial_templates/table_entry.html' with specification=specification id=group.name %}
{% endfor %} {% endfor %}
</div> </div>
......
...@@ -4,16 +4,22 @@ ...@@ -4,16 +4,22 @@
{% define "The work specification is now retrieving the necessary information." as badgeTitle %} {% define "The work specification is now retrieving the necessary information." as badgeTitle %}
{% elif status == "D" %} {% elif status == "D" %}
{% define "secondary" as badgecolor %} {% define "secondary" as badgecolor %}
{% define "The work specification is now being send to ATDB." as badgeTitle %} {% define "The work specification is now being sent to ATDB." as badgeTitle %}
{% elif status == "S" %} {% elif status == "S" %}
{% define "green" as badgecolor %} {% define "green" as badgecolor %}
{% define "The work specification has been succcesfully processed in ATDB." as badgeTitle %} {% define "The work specification has been succcesfully processed in ATDB." as badgeTitle %}
{% elif status == "E" %} {% elif status == "E" %}
{% define "red" as badgecolor %} {% define "red" as badgecolor %}
{% define "Retrieving the files to process or submitting the task to ATDB resulted in an error. Please reports this to the support helpdesk or try again." as badgeTitle %} {% define "Retrieving the files to process or submitting the task to ATDB resulted in an error. Please reports this to the support helpdesk or try again." as badgeTitle %}
{% else %} {% elif status == "R" %}
{% if number_of_files == 0 %}
{% define "orange" as badgecolor %}
{% define label|add:"*" as label %}
{% define "The work specification has been created, but does not contain any files. Please inspect the specified filters." as badgeTitle %}
{% elif number_of_files > 0 %}
{% define "primary" as badgecolor %} {% define "primary" as badgecolor %}
{% define "The work specification has been created but has not yet been send to ATDB for processing." as badgeTitle %} {% define "The work specification has been created, but has not yet been sent to ATDB for processing." as badgeTitle %}
{% endif %}
{% endif %} {% endif %}
<div class="badge badge--{{ badgecolor }} margin-top text text--capitalized" <div class="badge badge--{{ badgecolor }} margin-top text text--capitalized"
data-test-id="submission-status" data-test-id="submission-status"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
{% load workspecification %} {% load workspecification %}
<div class="table__row table__row--dark table__row--padding" <div class="table__row table__row--dark table__row--padding"
data-spec-id="specification.id"> data-test-id="specification-{{ specification.id }}">
<div class="table__cell table__cell--mini"> <div class="table__cell table__cell--mini">
<div class="flex-container flex-container--checkboxes"> <div class="flex-container flex-container--checkboxes">
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<div class="table__cell table__cell--truncate table__cell--horizontal-center">{{ specification.created_by }}</div> <div class="table__cell table__cell--truncate table__cell--horizontal-center">{{ specification.created_by }}</div>
<div class="table__cell table__cell--truncate table__cell--horizontal-center"> <div class="table__cell table__cell--truncate table__cell--horizontal-center">
{% include 'lofardata/partial_templates/badges.html' with status=specification.submission_status label=specification.get_submission_status_display %} {% include 'lofardata/partial_templates/badges.html' with status=specification.submission_status label=specification.get_submission_status_display number_of_files=specification.inputs.surls|length %}
</div> </div>
......
...@@ -57,14 +57,14 @@ ...@@ -57,14 +57,14 @@
<div class="table__cell">Splitted <div class="table__cell">Splitted
<a class="tooltip-dias tooltip-dias-bottom" <a class="tooltip-dias tooltip-dias-bottom"
data-tooltip="Wether or not the work specification has been splitted in multiple ATDB tasks and if that is recommended or not.">m</a> data-tooltip="Whether or not the work specification has been splitted in multiple ATDB tasks and if that is recommended or not.">m</a>
</div> </div>
<div class="table__cell">S <div class="table__cell">Suc
<a class="tooltip-dias tooltip-dias-bottom" <a class="tooltip-dias tooltip-dias-bottom"
data-tooltip="The successors of a work specification (i.e. the children of another work specification)">m</a> data-tooltip="The successors of a work specification (i.e. the children of another work specification)">m</a>
</div> </div>
<div class="table__cell">P <div class="table__cell">Pre
<a class="tooltip-dias tooltip-dias-bottom" <a class="tooltip-dias tooltip-dias-bottom"
data-tooltip="The predecessor of a work specification (i.e. the parent of a work specification)">m</a> data-tooltip="The predecessor of a work specification (i.e. the parent of a work specification)">m</a>
</div> </div>
......
{% load static %}
<a class="" title="User Guide" href="{% static 'documentation/docs/user-guide/'|add:link %}">
<span class="icon icon--primary icon--user">
<span class="icon icon--secondary icon--stacked icon--background--main icon--info-circle"></span>
</span>
</a>
\ No newline at end of file
...@@ -12,10 +12,14 @@ ...@@ -12,10 +12,14 @@
<a class="icon icon--times button--close" href="{% url 'index' %}"></a> <a class="icon icon--times button--close" href="{% url 'index' %}"></a>
<header class="flex-wrapper flex-wrapper--centered flex-wrapper--column"> <header class="flex-wrapper flex-wrapper--centered flex-wrapper--column">
{% if object.pk %} {% if object.pk %}
<h2 class="title text text--primary">Edit Work Specification {{ object.pk }}</h2> <h2 class="title text">Edit Work Specification {{ object.pk }}
{% include 'lofardata/partial_templates/user_guide_link.html' with link="work-specification/editing" %}
</h2>
<form method="post" action="{% url 'specification-update' object.pk %}"> <form method="post" action="{% url 'specification-update' object.pk %}">
{% else %} {% else %}
<h2 class="title text text--primary">New Work Specification </h2> <h2 class="title text text">New Work Specification
{% include 'lofardata/partial_templates/user_guide_link.html' with link="work-specification/creating" %}
</h2>
<form method="post" action="{% url 'specification-create' %}"> <form method="post" action="{% url 'specification-create' %}">
{% endif %} {% endif %}
...@@ -249,10 +253,10 @@ ...@@ -249,10 +253,10 @@
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js'></script> <script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js'></script>
<script type="text/javascript"> <script type="text/javascript">
const processingSite = "{% url 'processingsite-detail' 'replace' %}"; const processingSite = "{% url 'processingsite-detail' 'replace' %}";
const existingWorkflow = '{{ object.selected_workflow|default_if_none:"null" }}'; const existingWorkflow = '{{ object.selected_workflow|default_if_none:"" }}';
const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"null" }}'; const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"" }}';
const processingSites = {{ processing_sites | json }}; const processingSites = {{ processing_sites | json }};
const existingProcessingSite = '{{ object.processing_site.name }}'; const initialProcessingSite = '{{ object.processing_site.name|default:single_processing_site }}';
const groups = {{ groups | json }}; const groups = {{ groups | json }};
const existingGroup = '{{ object.group.id|default_if_none:"null" }}'; const existingGroup = '{{ object.group.id|default_if_none:"null" }}';
const pk = {{ object.pk|default_if_none:0 }}; const pk = {{ object.pk|default_if_none:0 }};
......
...@@ -175,7 +175,7 @@ ...@@ -175,7 +175,7 @@
<div class="table__row table__row--dark table__row--padding"> <div class="table__row table__row--dark table__row--padding">
<div class="table__cell table__cell--title">Submission status</div> <div class="table__cell table__cell--title">Submission status</div>
<div class="table__cell"> <div class="table__cell">
{% include '../partial_templates/badges.html' with status=object.submission_status label=object.get_submission_status_display %} {% include '../partial_templates/badges.html' with status=object.submission_status label=object.get_submission_status_display number_of_files=object.inputs.surls|length %}
</div> </div>
</div> </div>
......
...@@ -22,7 +22,9 @@ class GroupCreateUpdateView(LoginRequiredMixin, UpdateView): ...@@ -22,7 +22,9 @@ class GroupCreateUpdateView(LoginRequiredMixin, UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["processing_sites"] = list(ATDBProcessingSite.objects.values("name", "url")) processing_sites = list(ATDBProcessingSite.objects.values("name", "url"))
context["processing_sites"] = processing_sites
context["single_processing_site"] = processing_sites[0]['name'] if len(processing_sites) == 1 else None
return context return context
def form_valid(self, form): def form_valid(self, form):
......
...@@ -41,7 +41,9 @@ class WorkSpecificationCreateUpdateView(LoginRequiredMixin, CanEditWorkSpecifica ...@@ -41,7 +41,9 @@ class WorkSpecificationCreateUpdateView(LoginRequiredMixin, CanEditWorkSpecifica
except ObjectDoesNotExist: except ObjectDoesNotExist:
specification = None specification = None
context["filters"] = filters_preprocessor.preprocess(specification) context["filters"] = filters_preprocessor.preprocess(specification)
context["processing_sites"] = list(ATDBProcessingSite.objects.values("name", "url")) processing_sites = list(ATDBProcessingSite.objects.values("name", "url"))
context["processing_sites"] = processing_sites
context["single_processing_site"] = processing_sites[0]['name'] if len(processing_sites) == 1 else None
context["groups"] = list(Group.objects.values("name", "id", "processing_site", "selected_workflow", "selected_workflow_tag")) context["groups"] = list(Group.objects.values("name", "id", "processing_site", "selected_workflow", "selected_workflow_tag"))
return context return context
...@@ -143,5 +145,5 @@ class Overview(ListView): ...@@ -143,5 +145,5 @@ class Overview(ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["ungrouped_work_specifications"] = WorkSpecification.objects.filter(group=None) context["ungrouped_work_specifications"] = WorkSpecification.objects.filter(group=None).order_by('id')
return context return context
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment