From e2e0e419f15ba46497c68ae48ceefadeaf42cfdf Mon Sep 17 00:00:00 2001
From: Robbie Luijben <luijben@astron.nl>
Date: Thu, 23 Mar 2023 15:23:20 +0000
Subject: [PATCH] LDVSpec usability improvements

---
 .gitlab-ci.yml                                |  6 ++++
 integration/integration-test.robot            | 32 +++++++++++++++++++
 integration/resource/keywords.robot           | 12 +++++--
 integration/resource/variables.robot          |  1 +
 ldvspec/lofardata/models.py                   |  5 ++-
 ldvspec/lofardata/static/update_workflow.js   | 19 ++++++++---
 .../lofardata/group/create_update.html        | 10 ++++--
 .../lofardata/templates/lofardata/index.html  |  2 +-
 .../lofardata/partial_templates/badges.html   | 14 +++++---
 .../partial_templates/table_entry.html        |  4 +--
 .../partial_templates/table_header.html       |  6 ++--
 .../partial_templates/user_guide_link.html    |  7 ++++
 .../workspecification/create_update.html      | 14 +++++---
 .../lofardata/workspecification/detail.html   |  2 +-
 ldvspec/lofardata/views/group/template.py     |  4 ++-
 .../views/workspecification/template.py       |  6 ++--
 16 files changed, 114 insertions(+), 30 deletions(-)
 create mode 100644 ldvspec/lofardata/templates/lofardata/partial_templates/user_guide_link.html

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5f304516..602832e2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -69,6 +69,12 @@ integration-test:
     - docker compose build
     - docker compose up -d atdb-backend ldv-specification-backend
     - docker compose run integration
+  artifacts:
+    paths:
+      - /workdir/output.xml
+      - /workdir/log.html
+      - /workdir/report.html
+    expire_in: 1 week
   allow_failure: false
   rules:
     - when: on_success
diff --git a/integration/integration-test.robot b/integration/integration-test.robot
index 50c44589..01a6c9cf 100644
--- a/integration/integration-test.robot
+++ b/integration/integration-test.robot
@@ -56,3 +56,35 @@ Test create a group with a few work specifications
     Add Group   Beatles    1, 22, 13    3
     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
diff --git a/integration/resource/keywords.robot b/integration/resource/keywords.robot
index 817d1545..8b4d6600 100644
--- a/integration/resource/keywords.robot
+++ b/integration/resource/keywords.robot
@@ -13,7 +13,7 @@ Add Group
 
     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
     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
@@ -42,7 +42,7 @@ Add Work Specification
     [Arguments]    ${obs_id}
     Go To   ${LDVSPEC}specification/add/
     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
     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
@@ -93,7 +93,7 @@ Add LDV Data Product Filter
 Add ATDB Processing Site
     [Arguments]    ${token}
     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_access_token    ${token}
     Click Button    _save
@@ -109,6 +109,12 @@ Logout from LDV Spec
     Go To     ${LDVSPEC}accounts/logout/
     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
 
 Create Example Workflow in ATDB
diff --git a/integration/resource/variables.robot b/integration/resource/variables.robot
index 6ce6e6d4..94f9a075 100644
--- a/integration/resource/variables.robot
+++ b/integration/resource/variables.robot
@@ -3,3 +3,4 @@ ${BROWSER}    headlessfirefox
 ${LDVSPEC}    http://ldv-specification-backend:8000/ldvspec/
 ${LDVSPEC_API}    http://ldv-specification-backend:8000/ldvspec/api/
 ${ATDB}       http://atdb.backend:8000/atdb/
+${ATDB_SITE_NAME}    atdb_site
\ No newline at end of file
diff --git a/ldvspec/lofardata/models.py b/ldvspec/lofardata/models.py
index f36d9dd9..9884f266 100644
--- a/ldvspec/lofardata/models.py
+++ b/ldvspec/lofardata/models.py
@@ -124,6 +124,9 @@ class Group(models.Model):
     selected_workflow = 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):
     """Work Specification"""
@@ -139,7 +142,7 @@ class WorkSpecification(models.Model):
 
     # ATDB Workflow URL
     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
     atdb_task_ids = ArrayField(models.IntegerField(), null=True, blank=True)
diff --git a/ldvspec/lofardata/static/update_workflow.js b/ldvspec/lofardata/static/update_workflow.js
index 03cb8f88..bb85ac8c 100644
--- a/ldvspec/lofardata/static/update_workflow.js
+++ b/ldvspec/lofardata/static/update_workflow.js
@@ -14,7 +14,7 @@
             processingSites
         );
 
-        self.selectedProcessingSite = ko.observable(existingProcessingSite);
+        self.selectedProcessingSite = ko.observable(initialProcessingSite);
 
         self.selectedProcessingSite.subscribe((context) => {
             self.loadWorkflows();
@@ -26,9 +26,12 @@
 
         self.selectedGroup = ko.observable(existingGroup);
 
+        self.groupHasBeenSelectedAtLeastOnce = false;
+
         self.selectedGroup.subscribe((context) => {
             const group = self.groups() && self.groups().filter(group => group.id === self.selectedGroup())[0];
             if (group) {
+                self.groupHasBeenSelectedAtLeastOnce = true;
                 self.selectedProcessingSite(group.processing_site);
                 self.selectedTag(group.selected_workflow_tag);
                 self.selectedWorkflow(group.selected_workflow);
@@ -36,7 +39,10 @@
                 // setting the processing site to null will cause the loadWorkflows
                 // function to trigger, which in turn will clear the previously selected
                 // 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);
                 }
             }
@@ -62,8 +68,13 @@
                             self.selectedTag(group.selected_workflow_tag);
                             self.selectedWorkflow(group.selected_workflow);
                         } else {
-                            self.selectedTag(self.existing_workflow_tag);
-                            self.selectedWorkflow(self.existing_workflow);
+                            // auto select first tag if there is only 1, and there is no tag saved on the work specification yet
+                            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(() => {
                         self.isLoading(false);
diff --git a/ldvspec/lofardata/templates/lofardata/group/create_update.html b/ldvspec/lofardata/templates/lofardata/group/create_update.html
index 573686c5..9902e36f 100644
--- a/ldvspec/lofardata/templates/lofardata/group/create_update.html
+++ b/ldvspec/lofardata/templates/lofardata/group/create_update.html
@@ -12,10 +12,14 @@
                 <a class="icon icon--times button--close" href="{% url 'index' %}"></a>
                 <header class="flex-wrapper flex-wrapper--centered flex-wrapper--column">
                     {% 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 %}">
                     {% 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' %}">
                     {% endif %}
 
@@ -173,7 +177,7 @@
         const existingWorkflow = '{{ object.selected_workflow|default_if_none:"null" }}';
         const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"null" }}';
         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 existingGroup = null;
         const pk = {{ object.pk|default_if_none:0 }};
diff --git a/ldvspec/lofardata/templates/lofardata/index.html b/ldvspec/lofardata/templates/lofardata/index.html
index 88896013..e37aa980 100644
--- a/ldvspec/lofardata/templates/lofardata/index.html
+++ b/ldvspec/lofardata/templates/lofardata/index.html
@@ -48,7 +48,7 @@
                         <div class="table">
                             {% include 'lofardata/partial_templates/table_header.html' with id=group.name %}
                             <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 %}
                                 {% endfor %}
                             </div>
diff --git a/ldvspec/lofardata/templates/lofardata/partial_templates/badges.html b/ldvspec/lofardata/templates/lofardata/partial_templates/badges.html
index dc2f5a21..97fb33d1 100644
--- a/ldvspec/lofardata/templates/lofardata/partial_templates/badges.html
+++ b/ldvspec/lofardata/templates/lofardata/partial_templates/badges.html
@@ -4,16 +4,22 @@
     {% define "The work specification is now retrieving the necessary information." as badgeTitle %}
 {% elif status == "D" %}
     {% 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" %}
     {% define "green" as badgecolor %}
     {% define "The work specification has been succcesfully processed in ATDB." as badgeTitle %}
 {% elif status == "E" %}
     {% 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 %}
-{% else %}
-    {% define "primary" as badgecolor %}
-    {% define "The work specification has been created but has not yet been send to ATDB for processing." as badgeTitle %}
+{% 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 "The work specification has been created, but has not yet been sent to ATDB for processing." as badgeTitle %}
+    {% endif %}
 {% endif %}
 <div class="badge badge--{{ badgecolor }} margin-top text text--capitalized"
      data-test-id="submission-status"
diff --git a/ldvspec/lofardata/templates/lofardata/partial_templates/table_entry.html b/ldvspec/lofardata/templates/lofardata/partial_templates/table_entry.html
index 15cda1ed..c8973a19 100644
--- a/ldvspec/lofardata/templates/lofardata/partial_templates/table_entry.html
+++ b/ldvspec/lofardata/templates/lofardata/partial_templates/table_entry.html
@@ -3,7 +3,7 @@
 {% load workspecification %}
 
 <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="flex-container flex-container--checkboxes">
@@ -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">
-        {% 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>
 
 
diff --git a/ldvspec/lofardata/templates/lofardata/partial_templates/table_header.html b/ldvspec/lofardata/templates/lofardata/partial_templates/table_header.html
index 9baa9d94..4ef61360 100644
--- a/ldvspec/lofardata/templates/lofardata/partial_templates/table_header.html
+++ b/ldvspec/lofardata/templates/lofardata/partial_templates/table_header.html
@@ -57,14 +57,14 @@
 
         <div class="table__cell">Splitted
             <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 class="table__cell">S
+        <div class="table__cell">Suc
             <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>
         </div>
-        <div class="table__cell">P
+        <div class="table__cell">Pre
             <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>
         </div>
diff --git a/ldvspec/lofardata/templates/lofardata/partial_templates/user_guide_link.html b/ldvspec/lofardata/templates/lofardata/partial_templates/user_guide_link.html
new file mode 100644
index 00000000..1983e55c
--- /dev/null
+++ b/ldvspec/lofardata/templates/lofardata/partial_templates/user_guide_link.html
@@ -0,0 +1,7 @@
+{% 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
diff --git a/ldvspec/lofardata/templates/lofardata/workspecification/create_update.html b/ldvspec/lofardata/templates/lofardata/workspecification/create_update.html
index a6f689b9..70b77d9b 100644
--- a/ldvspec/lofardata/templates/lofardata/workspecification/create_update.html
+++ b/ldvspec/lofardata/templates/lofardata/workspecification/create_update.html
@@ -12,10 +12,14 @@
                 <a class="icon icon--times button--close" href="{% url 'index' %}"></a>
                 <header class="flex-wrapper flex-wrapper--centered flex-wrapper--column">
                     {% 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 %}">
                     {% 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' %}">
                     {% endif %}
 
@@ -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">
         const processingSite = "{% url 'processingsite-detail' 'replace' %}";
-        const existingWorkflow = '{{ object.selected_workflow|default_if_none:"null" }}';
-        const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"null" }}';
+        const existingWorkflow = '{{ object.selected_workflow|default_if_none:"" }}';
+        const existingWorkflowTag = '{{ object.selected_workflow_tag|default_if_none:"" }}';
         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 existingGroup = '{{ object.group.id|default_if_none:"null" }}';
         const pk = {{ object.pk|default_if_none:0 }};
diff --git a/ldvspec/lofardata/templates/lofardata/workspecification/detail.html b/ldvspec/lofardata/templates/lofardata/workspecification/detail.html
index b940d4d9..dbd74825 100644
--- a/ldvspec/lofardata/templates/lofardata/workspecification/detail.html
+++ b/ldvspec/lofardata/templates/lofardata/workspecification/detail.html
@@ -175,7 +175,7 @@
                             <div class="table__row table__row--dark table__row--padding">
                                 <div class="table__cell table__cell--title">Submission status</div>
                                 <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>
 
diff --git a/ldvspec/lofardata/views/group/template.py b/ldvspec/lofardata/views/group/template.py
index 09953ed2..b0f9360f 100644
--- a/ldvspec/lofardata/views/group/template.py
+++ b/ldvspec/lofardata/views/group/template.py
@@ -22,7 +22,9 @@ class GroupCreateUpdateView(LoginRequiredMixin, UpdateView):
 
     def get_context_data(self, **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
 
     def form_valid(self, form):
diff --git a/ldvspec/lofardata/views/workspecification/template.py b/ldvspec/lofardata/views/workspecification/template.py
index c1ca46c5..e3c27b2d 100644
--- a/ldvspec/lofardata/views/workspecification/template.py
+++ b/ldvspec/lofardata/views/workspecification/template.py
@@ -41,7 +41,9 @@ class WorkSpecificationCreateUpdateView(LoginRequiredMixin, CanEditWorkSpecifica
         except ObjectDoesNotExist:
             specification = None
         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"))
         return context
 
@@ -143,5 +145,5 @@ class Overview(ListView):
 
     def get_context_data(self, **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
-- 
GitLab