Skip to content
Snippets Groups Projects
Commit 75244aed authored by Fanna Lautenbach's avatar Fanna Lautenbach
Browse files

Merge branch 'SDC-810/group-creation' into 'main'

Add form, url, template and view; no functionality yet

See merge request !70
parents 779e2afa e1d416d1
No related branches found
No related tags found
1 merge request!70Add form, url, template and view; no functionality yet
Pipeline #42690 passed
from django.core.exceptions import ValidationError
from .models import WorkSpecification, DataProductFilter
from .models import WorkSpecification, DataProductFilter, Group
from django.forms import ModelForm
from django.forms import ModelForm, CharField
class WorkSpecificationForm(ModelForm):
......@@ -30,14 +30,14 @@ class WorkSpecificationForm(ModelForm):
class Meta:
model = WorkSpecification
fields = ['filters', 'selected_workflow', 'selected_workflow_tag', 'processing_site',
'predecessor_specification', 'batch_size',
'is_auto_submit']
'predecessor_specification', 'batch_size', 'group', 'is_auto_submit']
labels = {
'selected_workflow': 'Selected workflow',
'selected_workflow_tag': 'Workflow tag',
'processing_site': 'Processing Site (ATDB)',
'predecessor_specification': 'Predecessor',
'batch_size': 'Files per task',
'group': 'Group',
'is_auto_submit': 'Auto submit'
}
help_texts = {'selected_workflow': "The pipeline to run on the processing site.",
......@@ -45,5 +45,27 @@ class WorkSpecificationForm(ModelForm):
'processing_site': "The ATDB processing site to run a specific workflow in.",
'predecessor_specification': "The related predecessor of the current work specification.",
'batch_size': "The number of files every task generated by this work specification should have. Example: 10 files in total can be split into 5 tasks of 2 files.",
'group': "Include the work specification to a group. Note that it will inherit the group's processing site and workflow specifications.",
'is_auto_submit': "By checking this box, the work specification will be directly sent to the processing site and thus does not need inspection."
}
class GroupForm(ModelForm):
obs_ids = CharField(label='SAS IDs',
help_text="A list of SAS IDs separated with a comma. Example: 123, 456, 789",
required=True)
class Meta:
model = Group
fields = ['name', 'selected_workflow', 'selected_workflow_tag', 'processing_site', 'obs_ids']
labels = {
'selected_workflow': 'Selected workflow',
'selected_workflow_tag': 'Workflow tag',
'processing_site': 'Processing Site (ATDB)',
'name': 'Name',
}
help_texts = {'selected_workflow': "The pipeline to run on the processing site.",
'selected_workflow_tag': "The pipeline's tag as specified in ATDB.",
'processing_site': "The ATDB processing site to run a specific workflow in.",
'name': "A unique and custom name for this group"
}
{% extends 'lofardata/index.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% load define_action %}
{% load widget_tweaks %}
{% load json %}
{% block myBlock %}
<div class="overlay">
<div class="modal-dias-wrapper">
<div class="modal-dias modal-dias--fit-content">
<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 Group {{ object.pk }}</h2>
<form method="post" action="{% url 'group-update' object.pk %}">
{% else %}
<h2 class="title text text--primary">New Group </h2>
<form method="post" action="{% url 'group-create' %}">
{% endif %}
{% csrf_token %}
<div class="flex-wrapper flex-wrapper--row flex-wrapper--start">
<!-- Standard input fields -->
<div class="custom--div-margin">
<div class="flex-wrapper">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label">{{ form.name.label }}*</label>
<a class="tooltip-dias tooltip-dias-right custom--tooltip"
data-tooltip="{{ form.name.help_text }}">m</a>
</div>
<input class="input input--text margin-left margin-bottom"
id="id_name" name="name"
test-id="name"
placeholder="Enter a group name" type="text">
</div>
<div class="flex-wrapper flex-wrapper--row" id="div_id_processing_site">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label">{{ form.processing_site.label }}*</label>
<a class="tooltip-dias tooltip-dias-right custom--tooltip"
data-tooltip="{{ form.processing_site.help_text }}">m</a>
</div>
<div class="input-select-wrapper icon--inline icon-after icon-after--angle-down">
<select class="input input--select custom__input--fixed-width margin-left margin-bottom"
name="processing_site"
data-bind="options:processingSites,
optionsCaption: '---------',
optionsText: function(item) { return item.name + ' - ' + item.url},
optionsValue: 'name',
value: selectedProcessingSite"
test-id="processing_site"
></select>
</div>
</div>
<div class="flex-wrapper flex-wrapper--row" id="div_id_selected_workflow_tag">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label"
for="id_selected_workflow_tag">{{ form.selected_workflow_tag.label_tag }}*</label>
<a class="tooltip-dias tooltip-dias-right custom--tooltip"
data-tooltip="{{ form.selected_workflow_tag.help_text }}">m</a>
</div>
<div class="input-select-wrapper icon--inline icon-after icon-after--angle-down">
<select class="input input--select custom__input--fixed-width margin-bottom margin-left"
id="id_selected_workflow_tag"
test-id="workflow_tag"
name="selected_workflow_tag"
style="width: 12rem"
data-bind="options:tags,
optionsCaption: caption,
value: selectedTag,
disable: isLoading">
</select>
</div>
</div>
<div class="flex-wrapper flex-wrapper--row" id="div_id_selected_workflow">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label"
for="id_selected_workflow">{{ form.selected_workflow.label }}*</label>
<a class="tooltip-dias tooltip-dias-right custom--tooltip"
data-tooltip="{{ form.selected_workflow.help_text }}">m</a>
</div>
<div class="input-select-wrapper icon--inline icon-after icon-after--angle-down">
<select class="input input--select custom__input--fixed-width margin-bottom margin-left"
id="id_selected_workflow"
name="selected_workflow"
style="width: 12rem"
test-id="workflow"
data-bind="options:workflowsByTag,
optionsCaption: caption,
optionsValue: 'workflow_uri',
optionsText: 'workflow_uri',
value: selectedWorkflow,
disable: isLoading">
</select>
</div>
</div>
<div class="flex-wrapper">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label">{{ form.obs_ids.label }}*</label>
<a class="tooltip-dias tooltip-dias-right custom--tooltip"
data-tooltip="{{ form.obs_ids.help_text }}">m</a>
</div>
<input class="input input--text margin-left margin-bottom"
id="id_obs_ids" name="obs_ids"
test-id="obs_ids"
placeholder="Enter a a list of SAS IDs" type="text">
</div>
</div>
</div>
<div class="flex-wrapper flex-wrapper--centered margin-bottom margin-top">
<button class="button button--primary margin-right"
type="submit"
name="action"
value="Submit"
test-id="create-update"
title="
{% if object.pk %}
Update the task
{% else %}
Create the task to inspect the result. Send it later to ATDB.
{% endif %}">
{% if object.pk %}
Update
{% else %}
Create
{% endif %}
</button>
<a class="button button--red button--primary margin-left" href="{% url 'index' %}">Cancel</a>
</div>
</form>
</header>
{% if form.errors %}
<div class="popup-bar popup-bar--red">
<span class="icon icon--left icon--color-inherit icon--times"></span>
<div class="flex-wrapper flex-wrapper--column">
The input is invalid:
{% for field, errors in form.errors.items %}
{% for error in errors %}
{% if field == '__all__' %}
<li class="text text--red text--faded">{{ error }}</li>
{% else %}
<li class="text text--red text--faded">{{ field }}: {{ error }}</li>
{% endif %}
{% endfor %}
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<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 processingSites = {{ processing_sites | json }};
const existingProcessingSite = '{{ object.processing_site.name }}';
</script>
<script src="{% static 'update_workflow.js' %}"></script>
{% endblock %}
......@@ -13,6 +13,11 @@
title="Create a new work specification"
href="{% url 'specification-create' %}">
<span class="icon icon--plus"></span></a>
<a class="button button--secondary button--icon-button margin-left"
style="display: none"
title="Create a new group"
href="{% url 'group-create' %}">
<span class="icon icon--layer-plus"></span></a>
</div>
<div class="table">
......
......@@ -24,6 +24,18 @@
<!-- Standard input fields -->
<div class="custom--div-margin">
<h3 class="text text--primary text--title">Selection</h3>
<div class="flex-wrapper flex-wrapper--row" id="div_id_group" style="display: none">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label"
for="id_group">{{ form.group.label }}</label>
<a class="tooltip-dias tooltip-dias-right custom--tooltip"
data-tooltip="{{ form.group.help_text }}">m</a>
</div>
<div class="input-select-wrapper icon--inline icon-after icon-after--angle-down margin-left margin-bottom">
{% render_field form.group class="input input--select custom__input--fixed-width" %}
</div>
</div>
<div class="flex-wrapper flex-wrapper--row" id="div_id_processing_site">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label">{{ form.processing_site.label }}*</label>
......@@ -89,7 +101,6 @@
</div>
</div>
<div class="flex-wrapper flex-wrapper--row" id="div_id_predecessor">
<div class="flex-wrapper flex-wrapper--row custom__input--fixed-min-width">
<label class="input__label"
......
import unittest
from lofardata.models import WorkSpecification, Group
class WorkSpecificationCreation(unittest.TestCase):
def test_create_group_with_one_obs_id(self):
pass
def test_create_group_with_multiple_obs_ids(self):
pass
......@@ -32,6 +32,8 @@ urlpatterns = [
# GUI
path('', views.Specifications.as_view(), name='index'),
path('api/', views.api, name='api'),
path('specification/<int:pk>/', views.WorkSpecificationDetailView.as_view(), name='specification-detail'),
path('specification/add/', views.WorkSpecificationCreateUpdateView.as_view(), name='specification-create'),
path('specification/update/<int:pk>/', views.WorkSpecificationCreateUpdateView.as_view(), name='specification-update'),
......@@ -40,6 +42,8 @@ urlpatterns = [
path('specification/tasks/<int:pk>/', views.WorkSpecificationATDBTasksView.as_view(), name='specification-tasks'),
path('specification/dataset-size-info/<int:pk>/', views.WorkSpecificationDatasetSizeInfoView.as_view(), name='dataset-size-info'),
path('specification/dataproducts/<int:pk>', views.DataProductViewPerSasID.as_view(), name='specification-dataproducts'),
path('group/add/', views.GroupCreateUpdateView.as_view(), name='group-create')
# Workaround for injecting the urls from the ModelViewSet, which requires a "Router"
......
......@@ -14,13 +14,14 @@ from rest_framework.response import Response
from rest_framework.reverse import reverse_lazy
from rest_framework.schemas.openapi import AutoSchema
from .forms import WorkSpecificationForm
from .forms import WorkSpecificationForm, GroupForm
from .mixins import CanAccessWorkSpecificationMixin
from .models import (
ATDBProcessingSite,
DataProduct,
DataProductFilter,
WorkSpecification,
Group,
)
from .serializers import (
ATDBProcessingSiteSerializer,
......@@ -66,12 +67,14 @@ def api(request):
atdb_hosts = ATDBProcessingSite.objects.values("name", "url")
return render(request, "lofardata/api.html", {"atdb_hosts": atdb_hosts})
def set_post_submit_values(specification, user):
specification.async_task_result = None
specification.is_ready = False
if specification.created_by is None:
specification.created_by = user
class Specifications(ListView):
serializer_class = WorkSpecificationSerializer
template_name = "lofardata/index.html"
......@@ -224,6 +227,7 @@ class DataProductViewPerSasID(LoginRequiredMixin, CanAccessWorkSpecificationMixi
def get_object(self):
return WorkSpecification.objects.get(pk=self.kwargs['pk'])
# ---------- REST API views ----------
class DataProductView(generics.ListCreateAPIView):
model = DataProduct
......@@ -289,3 +293,21 @@ class WorkSpecificationViewset(viewsets.ModelViewSet):
time.sleep(1) # allow for some time to pass
return redirect("index")
class GroupCreateUpdateView(LoginRequiredMixin, UpdateView):
template_name = "lofardata/group/create_update.html"
model = Group
form_class = GroupForm
def get_object(self, queryset=None):
if self.kwargs.__len__() == 0 or self.kwargs["pk"] is None:
group = Group()
else:
group = Group.objects.get(pk=self.kwargs["pk"])
return group
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["processing_sites"] = list(ATDBProcessingSite.objects.values("name", "url"))
return context
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