diff --git a/ldvspec/Dockerfile b/ldvspec/Dockerfile
index c6db5f535ee6b668fcc701da98eebc8a7d8df8c0..042fdd888b7fd9daec810625b5e94986d7a58a26 100644
--- a/ldvspec/Dockerfile
+++ b/ldvspec/Dockerfile
@@ -13,6 +13,7 @@ RUN \
  apk --purge del .build-deps
 
 # collect the static files and make sure that the latest database migrations are done
+RUN date +"(%d %b %Y - %H:%M)" >> VERSION
 RUN python manage.py collectstatic --settings=ldvspec.settings.dev --noinput
 
 # run gunicorn
diff --git a/ldvspec/VERSION b/ldvspec/VERSION
new file mode 100644
index 0000000000000000000000000000000000000000..89589ba54884b40ba703e87e01624929833c9d8e
--- /dev/null
+++ b/ldvspec/VERSION
@@ -0,0 +1 @@
+Version 1.0.0
\ No newline at end of file
diff --git a/ldvspec/ldvspec/context_processors.py b/ldvspec/ldvspec/context_processors.py
new file mode 100644
index 0000000000000000000000000000000000000000..070d722adf6b695251a4d9e6962e76983ce2dfe1
--- /dev/null
+++ b/ldvspec/ldvspec/context_processors.py
@@ -0,0 +1,5 @@
+from django.conf import settings
+
+
+def version_string(_):
+    return {'VERSION_STRING': settings.VERSION_STR}
diff --git a/ldvspec/ldvspec/settings/base.py b/ldvspec/ldvspec/settings/base.py
index 8d54a412ed14e168cb05b599ea4f06ccdd330d7c..049a53aad3744db5212bf8ae5e884efe4ac01b8a 100644
--- a/ldvspec/ldvspec/settings/base.py
+++ b/ldvspec/ldvspec/settings/base.py
@@ -3,6 +3,7 @@ Django settings for ldvspec project.
 """
 import os
 from pathlib import Path
+import logging
 
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
 BASE_DIR = Path(__file__).resolve().parent.parent
@@ -21,7 +22,6 @@ except:
 
 ALLOWED_HOSTS = []
 
-
 # Application definition
 
 INSTALLED_APPS = [
@@ -37,9 +37,14 @@ INSTALLED_APPS = [
     'corsheaders',
     'django_filters',
     'django_extensions',
-    'uws'
+    'uws',
+    'crispy_forms',
+    'crispy_bootstrap5',
 ]
 
+CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
+
+CRISPY_TEMPLATE_PACK = "bootstrap5"
 MIDDLEWARE = [
     'corsheaders.middleware.CorsMiddleware',
     'django.middleware.security.SecurityMiddleware',
@@ -65,6 +70,7 @@ TEMPLATES = [
                 'django.template.context_processors.request',
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
+                'ldvspec.context_processors.version_string'
             ],
         },
     },
@@ -72,7 +78,6 @@ TEMPLATES = [
 
 WSGI_APPLICATION = 'ldvspec.wsgi.application'
 
-
 # Password validation
 # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
 
@@ -91,7 +96,6 @@ AUTH_PASSWORD_VALIDATORS = [
     },
 ]
 
-
 # Internationalization
 # https://docs.djangoproject.com/en/4.0/topics/i18n/
 
@@ -103,7 +107,6 @@ USE_I18N = True
 
 USE_TZ = True
 
-
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/4.0/howto/static-files/
 
@@ -131,8 +134,15 @@ REST_FRAMEWORK = {
     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
     'PAGE_SIZE': 100
 }
-
+LOGIN_REDIRECT_URL = '/ldvspec'
 # Recommended to use an environment variable to set the broker URL.
 CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL", "amqp://guest@localhost:5672")
-
-UWS_WORKERS = ['lofardata.workers.query.Echo']
\ No newline at end of file
+UWS_WORKERS = ['lofardata.workers.query.Echo']
+
+VERSION_STR = 'Version 1.0.0 (Unknown)'
+version_file_path = BASE_DIR.parent / 'VERSION'
+if os.path.exists(version_file_path):
+    with open(version_file_path, 'r') as f_stream:
+        VERSION_STR = f_stream.read().strip()
+else:
+    logging.warning('Cannot find version file %s', version_file_path)
diff --git a/ldvspec/ldvspec/settings/dev.py b/ldvspec/ldvspec/settings/dev.py
index 49cd8b50b60f8e4c93a4a980d7eb5d68def29a1b..e5ae158c35da0876de08daba963086676853e29a 100644
--- a/ldvspec/ldvspec/settings/dev.py
+++ b/ldvspec/ldvspec/settings/dev.py
@@ -23,3 +23,5 @@ DATABASES = {
 
 AUTH_PASSWORD_VALIDATORS = []
 
+# bypass celery async workers
+#CELERY_TASK_ALWAYS_EAGER=True
\ No newline at end of file
diff --git a/ldvspec/ldvspec/urls.py b/ldvspec/ldvspec/urls.py
index 50e17ceaf8ab1d38a69ab4f16f7f466dfb605471..d714ce311875a6319c9b02aeeb4954f30d541d9f 100644
--- a/ldvspec/ldvspec/urls.py
+++ b/ldvspec/ldvspec/urls.py
@@ -18,6 +18,6 @@ from django.urls import include, path
 
 urlpatterns = [
     path('ldvspec/', include('lofardata.urls')),
-    path('ldvspec/admin/', admin.site.urls),
+    path('ldvspec/admin/', admin.site.urls, name='admin_app'),
     path('ldvspec/api-auth/', include('rest_framework.urls')),
 ]
diff --git a/ldvspec/lofardata/admin.py b/ldvspec/lofardata/admin.py
index 96c7adccc48beb8ea66b0160a75757b740106de9..41676f558be3444e238ef5ba4b76c6d2b0c564c0 100644
--- a/ldvspec/lofardata/admin.py
+++ b/ldvspec/lofardata/admin.py
@@ -1,8 +1,10 @@
 from django.contrib import admin
 
 # Register your models here.
-from .models import DataProduct, DataProductFilter, ATDBProcessingSite
+from .models import DataProduct, DataProductFilter, ATDBProcessingSite, WorkSpecification, DataLocation
 
 admin.site.register(DataProduct)
 admin.site.register(DataProductFilter)
+admin.site.register(DataLocation)
 admin.site.register(ATDBProcessingSite)
+admin.site.register(WorkSpecification)
\ No newline at end of file
diff --git a/ldvspec/lofardata/forms.py b/ldvspec/lofardata/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3adf6c2623e79800ae1fdf20037111322407de1
--- /dev/null
+++ b/ldvspec/lofardata/forms.py
@@ -0,0 +1,20 @@
+from django import forms
+from django.forms import ModelForm
+from .models import WorkSpecification
+
+
+class WorkSpecificationForm(ModelForm):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields['inputs'].required = False
+    class Meta:
+        model = WorkSpecification
+        fields = ['filters', 'selected_workflow', 'processing_site', 'inputs', 'batch_size']
+        labels = {
+            'filters': 'Filters',
+            'selected_workflow': 'Selected workflow',
+            'processing_site': 'Processing Site (ATDB)',
+            'inputs': 'Inputs',
+            'batch_size': '# of files per ATDB tasks'
+        }
diff --git a/ldvspec/lofardata/migrations/0008_update_models_to_deal_with_gui.py b/ldvspec/lofardata/migrations/0008_update_models_to_deal_with_gui.py
new file mode 100644
index 0000000000000000000000000000000000000000..41002ba5826309f7e1b84c8e0653d915da49f5f0
--- /dev/null
+++ b/ldvspec/lofardata/migrations/0008_update_models_to_deal_with_gui.py
@@ -0,0 +1,56 @@
+# Generated by Django 3.2 on 2022-09-14 10:44
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('lofardata', '0008_atdbprocessingsite_access_token'), ('lofardata', '0009_dataproductfilter_filter_type'), ('lofardata', '0009_auto_20220906_0856'), ('lofardata', '0010_merge_20220908_0923'), ('lofardata', '0011_workspecification_predecessor_specification')]
+
+    dependencies = [
+        ('lofardata', '0007_workspecification_selected_workflow'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='atdbprocessingsite',
+            name='access_token',
+            field=models.CharField(max_length=1000, null=True),
+        ),
+        migrations.AddField(
+            model_name='dataproductfilter',
+            name='filter_type',
+            field=models.CharField(choices=[('Dropdown', 'Dropdown'), ('Free', 'Freeform')], default='Free', max_length=20),
+        ),
+        migrations.AddField(
+            model_name='workspecification',
+            name='batch_size',
+            field=models.IntegerField(default=0),
+        ),
+        migrations.AddField(
+            model_name='workspecification',
+            name='is_auto_submit',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AddField(
+            model_name='workspecification',
+            name='predecessor_task',
+            field=models.IntegerField(null=True),
+        ),
+        migrations.AddField(
+            model_name='workspecification',
+            name='purge_policy',
+            field=models.CharField(choices=[('yes', 'yes'), ('no', 'no'), ('do', 'do')], default='no', max_length=16),
+        ),
+        migrations.AddField(
+            model_name='workspecification',
+            name='submission_status',
+            field=models.CharField(choices=[('N', 'not submitted'), ('S', 'submitted'), ('D', 'defining'), ('E', 'error')], default='N', max_length=1),
+        ),
+        migrations.AddField(
+            model_name='workspecification',
+            name='predecessor_specification',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='lofardata.workspecification'),
+        ),
+    ]
diff --git a/ldvspec/lofardata/migrations/0009_dataproductfilter_filter_type.py b/ldvspec/lofardata/migrations/0009_dataproductfilter_filter_type.py
new file mode 100644
index 0000000000000000000000000000000000000000..b862eb5f73e8b74311d838737da23ae9d6f88914
--- /dev/null
+++ b/ldvspec/lofardata/migrations/0009_dataproductfilter_filter_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2 on 2022-09-07 07:57
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('lofardata', '0008_atdbprocessingsite_access_token'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='dataproductfilter',
+            name='filter_type',
+            field=models.CharField(choices=[('Dropdown', 'Dropdown'), ('Free', 'Freeform')], default='Free', max_length=20),
+        ),
+    ]
diff --git a/ldvspec/lofardata/migrations/0010_merge_20220908_0923.py b/ldvspec/lofardata/migrations/0010_merge_20220908_0923.py
new file mode 100644
index 0000000000000000000000000000000000000000..d51b7d26303477c45bf2976664094ec30b799277
--- /dev/null
+++ b/ldvspec/lofardata/migrations/0010_merge_20220908_0923.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.2 on 2022-09-08 09:23
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('lofardata', '0009_auto_20220906_0856'),
+        ('lofardata', '0009_dataproductfilter_filter_type'),
+    ]
+
+    operations = [
+    ]
diff --git a/ldvspec/lofardata/migrations/0011_workspecification_predecessor_specification.py b/ldvspec/lofardata/migrations/0011_workspecification_predecessor_specification.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c141f514be8db414a01e762b307fb1bb83f82a6
--- /dev/null
+++ b/ldvspec/lofardata/migrations/0011_workspecification_predecessor_specification.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.2 on 2022-09-08 14:36
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('lofardata', '0010_merge_20220908_0923'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='workspecification',
+            name='predecessor_specification',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='lofardata.workspecification'),
+        ),
+    ]
diff --git a/ldvspec/lofardata/models.py b/ldvspec/lofardata/models.py
index 15f513231024e81455c74f684f3d26c4ea6312be..428da1d34b670796809501470abbc2559cfcc0e3 100644
--- a/ldvspec/lofardata/models.py
+++ b/ldvspec/lofardata/models.py
@@ -49,15 +49,15 @@ class DataProduct(models.Model):
 
     @staticmethod
     def insert_dataproduct(
-        obs_id,
-        oid_source,
-        dataproduct_source,
-        dataproduct_type,
-        project,
-        activity,
-        surl,
-        filesize,
-        additional_meta,
+            obs_id,
+            oid_source,
+            dataproduct_source,
+            dataproduct_type,
+            project,
+            activity,
+            surl,
+            filesize,
+            additional_meta,
     ):
         scheme, netloc, *_ = urlsplit(surl)
 
@@ -79,17 +79,30 @@ class DataProduct(models.Model):
         return dp
 
 
+class DataFilterType(models.TextChoices):
+    DROPDOWN = 'Dropdown', _('Dropdown')
+    FREEFORM = 'Free', _('Freeform')
+
+
 class DataProductFilter(models.Model):
     field = models.CharField(max_length=100)
     name = models.CharField(max_length=20)
     lookup_type = models.CharField(max_length=100)
 
+    filter_type = models.CharField(max_length=20, choices=DataFilterType.choices, default=DataFilterType.FREEFORM)
+
+    def __str__(self):
+        return f'({self.pk}) "{self.name}" [{self.field} ->{self.lookup_type}]'
+
 
 class ATDBProcessingSite(models.Model):
     name = models.CharField(primary_key=True, max_length=100)
     url = models.URLField()
     access_token = models.CharField(max_length=1000, null=True)
 
+    def __str__(self):
+        return str(self.name) + ' - ' + str(self.url)
+
 
 class SUBMISSION_STATUS(models.TextChoices):
     """Status of Work Specifcation to ATDB"""
@@ -127,6 +140,8 @@ class WorkSpecification(models.Model):
     # Task ID's that were created in ATDB
     related_tasks = ArrayField(models.IntegerField(), null=True)
     predecessor_task = models.IntegerField(null=True)
+    predecessor_specification = models.ForeignKey('self', null=True, on_delete=models.DO_NOTHING,
+                                                  related_name='successor')
 
     # The query for gathering files has been executed
     is_ready = models.BooleanField(default=False)
@@ -143,6 +158,9 @@ class WorkSpecification(models.Model):
         max_length=16, choices=PURGE_POLICY.choices, default=PURGE_POLICY.NO
     )
 
+    def __str__(self):
+        return str(self.id) + ' - ' + str(self.filters) + " (" + str(self.created_on) + ")"
+
     # How many files per task. 0 is single task with all files
     batch_size = models.IntegerField(default=0, null=False, blank=False)
     submission_status = models.CharField(
@@ -152,7 +170,7 @@ class WorkSpecification(models.Model):
     )
 
     def save(
-        self, force_insert=False, force_update=False, using=None, update_fields=None
+            self, force_insert=False, force_update=False, using=None, update_fields=None
     ):
         super(WorkSpecification, self).save(
             force_insert=force_insert,
diff --git a/ldvspec/lofardata/serializers.py b/ldvspec/lofardata/serializers.py
index e04ceba8c8e40c338f76461fa6e9f7c0e80da0f0..e45fa2ed30c62282c66ba37e1b4b7950d576e9c8 100644
--- a/ldvspec/lofardata/serializers.py
+++ b/ldvspec/lofardata/serializers.py
@@ -1,7 +1,13 @@
 from abc import ABC
 
 from rest_framework import serializers
-from .models import DataProduct, DataLocation, WorkSpecification
+from .models import DataProduct, DataLocation, WorkSpecification, ATDBProcessingSite
+
+
+class ATDBProcessingSiteSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = ATDBProcessingSite
+        fields = '__all__'
 
 
 class DataLocationSerializer(serializers.ModelSerializer):
diff --git a/ldvspec/lofardata/static/lofardata/style.css b/ldvspec/lofardata/static/lofardata/style.css
index 86e9ac908dc232210c22807bcabfd36f7bd2efe7..5def068edefb55285a94b216c22b3bff8933791c 100644
--- a/ldvspec/lofardata/static/lofardata/style.css
+++ b/ldvspec/lofardata/static/lofardata/style.css
@@ -1,111 +1,116 @@
 TD {
-  font-family: Raleway;
-  font-size: 12pt;
+    font-family: Raleway, serif;
+    font-size: 12pt;
 }
 
-.defining,.staging,.fetching,.processing,.storing,.scrub,.scrubbing,.archiving {
-  font-style: italic;
-  color: green;
+.defining, .staging, .fetching, .processing, .storing, .scrub, .scrubbing, .archiving {
+    font-style: italic;
+    color: green;
 }
 
-.defined,.staged,.fetched,.processed,.stored,.validated,.scrubbed,.archived,.finished {
-  background-color: lemonchiffon;
-  color: blue;
+.defined, .staged, .fetched, .processed, .stored, .validated, .scrubbed, .archived, .finished {
+    background-color: lemonchiffon;
+    color: blue;
 }
 
 .active {
-  background-color: lemonchiffon;
+    background-color: lemonchiffon;
 }
 
 .max {
-   font-weight: bold;
-   background-color: lightgreen;
+    font-weight: bold;
+    background-color: lightgreen;
 }
 
 .aggregate {
-  font-weight: bold;
-  background-color: lightgreen;
+    font-weight: bold;
+    background-color: lightgreen;
 }
 
 .aggregate_failed {
-  font-weight: bold;
-  color: red;
-  background-color: lightgreen;
+    font-weight: bold;
+    color: red;
+    background-color: lightgreen;
 }
 
 
-.error,.failed,.staging_failed,.staged_failed,.processed_failed,.scrubbed_failed,.stored_failed,.archived_failed,.on_hold {
-  color: red;
-  font-weight: bold;
+.error, .failed, .staging_failed, .staged_failed, .processed_failed, .scrubbed_failed, .stored_failed, .archived_failed, .on_hold {
+    color: red;
+    font-weight: bold;
 }
 
-.processed,.ok,.running {
- color: green;
- font-weight: bold;
+.processed, .ok, .running {
+    color: green;
+    font-weight: bold;
 }
 
-.scrubbed, {
-  color: darkgray;
-  font-style: italic;
+.scrubbed {
+    color: darkgray;
+    font-style: italic;
 }
 
-.processing,.processing_copying {
- font-weight: bold;
- background-color: lightyellow;
+.processing, .processing_copying {
+    font-weight: bold;
+    background-color: lightyellow;
 }
 
 p.title {
-	font-family: Raleway;
-	font-size: 18pt;
+    font-family: Raleway, serif;
+    font-size: 18pt;
 }
 
 .footer {
-  font-family: Arial;
-  font-size: 10pt;
-  font-style: italic;
+    font-family: Arial, serif;
+    font-size: 10pt;
+    font-style: italic;
 }
 
 .ml-auto .dropdown-menu {
-  left: auto !important;
-  right: 0px;
+    left: auto !important;
+    right: 0;
 }
 
 .bigger {
-   font-size: 13pt;
+    font-size: 13pt;
 }
 
 .info {
-   background-color: #E0F8F8;
+    background-color: #E0F8F8;
 }
 
 .form-signin {
-  width: 100%;
-  max-width: 330px;
-  padding: 15px;
-  margin: 0 auto;
+    width: 100%;
+    max-width: 330px;
+    padding: 15px;
+    margin: 0 auto;
 }
+
 .form-signin .checkbox {
-  font-weight: 400;
+    font-weight: 400;
 }
+
 .form-signin .form-control {
-  position: relative;
-  box-sizing: border-box;
-  height: auto;
-  padding: 10px;
-  font-size: 16px;
+    position: relative;
+    box-sizing: border-box;
+    height: auto;
+    padding: 10px;
+    font-size: 16px;
 }
+
 .form-signin .form-control:focus {
-  z-index: 2;
+    z-index: 2;
 }
+
 .form-signin input[type="email"] {
-  margin-bottom: -1px;
-  border-bottom-right-radius: 0;
-  border-bottom-left-radius: 0;
+    margin-bottom: -1px;
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
 }
+
 .form-signin input[type="password"] {
-  margin-bottom: 10px;
-  border-top-left-radius: 0;
-  border-top-right-radius: 0;
+    margin-bottom: 10px;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
 }
 
 .modal-body {
@@ -122,4 +127,56 @@ p.title {
 
 .form-flex {
     display: flex;
+}
+
+footer {
+    text-align: center;
+    padding: .4rem;
+    vertical-align: center;
+    display: block;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    background-color: #ded;
+    width: 100%;
+
+    -webkit-box-shadow: 0 -2px 3px rgba(50, 50, 50, 0.75);
+    -moz-box-shadow: 0 -2px 3px rgba(50, 50, 50, 0.75);
+    box-shadow: 0 -2px 3px rgba(50, 50, 50, 0.75);
+}
+
+footer p {
+    margin: auto;
+    text-shadow: 0 0 25px lightgrey;
+}
+
+pre {
+    border-radius: 1rem;
+    padding: 1rem;
+    box-shadow: 2px 2px forestgreen;
+    color: black;
+    font-size: larger;
+    background-color: palegreen;
+}
+
+#div_id_filters label {
+    display: none;
+}
+
+#div_id_filters textarea {
+    width: 20rem;
+    pointer-events: none;
+    cursor: not-allowed;
+    color: gray;
+    background-color: lightgrey;
+    margin: 1rem auto;
+}
+
+.info-text {
+    font-size: 2rem;
+    margin-bottom: 2rem;
+}
+
+.bottom-bar {
+    justify-content: center;
 }
\ No newline at end of file
diff --git a/ldvspec/lofardata/tasks.py b/ldvspec/lofardata/tasks.py
index 148097203af20b8f310dc1e6699474ed10cf3dec..20b160d415f63938d2f95bdd27794f3fc8af5dcc 100644
--- a/ldvspec/lofardata/tasks.py
+++ b/ldvspec/lofardata/tasks.py
@@ -47,7 +47,10 @@ def define_work_specification(workspecification_id):
             for dataproduct in dataproducts
         ]
     }
-    specification.inputs = inputs
+    if specification.inputs is None:
+        specification.inputs = inputs
+    else:
+        specification.inputs.update(inputs)
     specification.is_ready = True
     specification.save()
 
diff --git a/ldvspec/lofardata/templates/base.html b/ldvspec/lofardata/templates/base.html
deleted file mode 100644
index e5ab1386442bf38a9a3f6bfd4fece547a9658fc5..0000000000000000000000000000000000000000
--- a/ldvspec/lofardata/templates/base.html
+++ /dev/null
@@ -1,56 +0,0 @@
-
-<!DOCTYPE html>
-{% load static %}
-
-<html lang="en">
-<head>
-
-    <!-- Required meta tags -->
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-
-    <title>{% block myBlockTitle %}LDV Specification{% endblock %}</title>
-
-    <!-- loads the path to static files -->
-    <link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
-    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
-
-    <link rel="stylesheet" type="text/css" href="{% static 'lofardata/style.css' %}"/>
-    <link rel="icon" href="{% static 'favicon.ico' %}">
-
-    {% block extra_js %}{% endblock %}
-
-
-</head>
-<body>
-    <nav class="navbar navbar-expand-lg navbar-light bg-light">
-        <div class="container-fluid">
-            <ul class="nav navbar-nav">
-            <!-- Header -->
-                <li><a class="navbar-brand" href="{% url 'index' %}">
-                     <img src="{% static 'lofardata/ldvspec_logo.png' %}"  height="30" alt="">
-                    &nbsp;Specification Service</a>
-                </li>
-
-                <li><a class="nav-link" href="{% url 'index' %}">Home</a></li>
-                <li><a class="nav-link" href="{% url 'dataproduct' %}">LOFAR Data</a></li>
-
-            {% if user.is_authenticated %}
-                <a class="nav-link" href="{% url 'logout' %}" target="_blank">Logout {{ user.get_username }}</a>
-            {% endif %}
-            {% if not user.is_authenticated %}
-                <a class="nav-link" href="{% url 'login' %}" target="_blank">Login</a>
-            {% endif %}
-        </div>
-
-    </nav>
-
-    <!-- to add blocks of code -->
-    {% block myBlock %}
-    {% endblock %}
-
-</body>
-
-
-</html>
diff --git a/ldvspec/lofardata/templates/lofardata/api.html b/ldvspec/lofardata/templates/lofardata/api.html
new file mode 100644
index 0000000000000000000000000000000000000000..1531ab68e54d5e892985a442f9101be5c1495fa9
--- /dev/null
+++ b/ldvspec/lofardata/templates/lofardata/api.html
@@ -0,0 +1,34 @@
+{% extends 'lofardata/base.html' %}
+{% load static %}
+{% load crispy_forms_tags %}
+
+{% block myBlock %}
+
+<div class="container-fluid details-container">
+
+    <table class="table table-striped table-bordered table-sm">
+
+        <tbody>
+
+            <tr><td>atdb_host</td>
+                <td><ul>
+                {%  for atdb_host in atdb_hosts %}
+                    <li>{{ atdb_host.name }} ( <a href="{{ atdb_host.url }}">{{ atdb_host.url }}</a> )</li>
+                {% endfor %}
+                </ul>
+                </td>
+            </tr>
+            <tr><td>api data</td><td><a href="{% url 'dataproduct' %}">{% url 'dataproduct' %}</a></td></tr>
+            <tr><td>api data-location</td><td><a href="{% url 'datalocation' %}">{% url 'datalocation' %}</a></td></tr>
+            <tr><td>api work-specification</td><td><a href="{% url 'workspecification-list' %}">{% url 'workspecification-list' %}</a></td></tr>
+
+            <tr><td>api-schema</td><td><a href="{% url 'openapi-schema' %}">{% url 'openapi-schema' %}</a></td></tr>
+        </tbody>
+
+    </table>
+</div>
+
+
+
+{% endblock %}
+
diff --git a/ldvspec/lofardata/templates/lofardata/base.html b/ldvspec/lofardata/templates/lofardata/base.html
new file mode 100644
index 0000000000000000000000000000000000000000..66f3bd0ecaffbece17136d6735b6e7e7035ce4c0
--- /dev/null
+++ b/ldvspec/lofardata/templates/lofardata/base.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+{% load static %}
+
+<html lang="en">
+<head>
+
+    <!-- Required meta tags -->
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+    <title>{% block myBlockTitle %}LDV Specification{% endblock %}</title>
+
+    <!-- loads the path to static files -->
+    <link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
+
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
+          integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
+            integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
+            crossorigin="anonymous"></script>
+    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css"
+          integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous">
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
+
+    <link rel="stylesheet" type="text/css" href="{% static 'lofardata/style.css' %}"/>
+    <link rel="icon" href="{% static 'favicon.ico' %}">
+
+    {% block extra_js %}{% endblock %}
+
+
+</head>
+<body>
+<nav class="navbar navbar-expand-lg navbar-light bg-light">
+    <div class="container-fluid">
+        <ul class="nav navbar-nav">
+            <!-- Header -->
+            <li><a class="navbar-brand" href="{% url 'index' %}">
+                <img src="{% static 'lofardata/ldvspec_logo.png' %}" height="30" alt="">
+                &nbsp;Specification Service</a>
+            </li>
+
+            <li><a class="nav-link" href="{% url 'index' %}">Home</a></li>
+            {% if user.is_authenticated %}
+                <li><a class="nav-link" href="{% url 'specification' %}">Specification</a></li>
+            {% endif %}
+            <li><a class="nav-link" href="{% url 'api' %}">API</a></li>
+            <li><a class="nav-link" href="{% url 'admin:index' %}" target="_blank">Admin Page</a></li>
+
+            {% if user.is_authenticated %}
+                <a class="nav-link" href="{% url 'logout' %}" target="_blank">Logout ({{ user.get_username }})</a>
+            {% endif %}
+            {% if not user.is_authenticated %}
+                <a class="nav-link" href="{% url 'login' %}" target="_blank">Login</a>
+            {% endif %}
+    </div>
+
+</nav>
+
+<!-- to add blocks of code -->
+{% block myBlock %}
+{% endblock %}
+
+<footer>
+    <p> {{ VERSION_STRING }} </p>
+</footer>
+</body>
+
+
+</html>
diff --git a/ldvspec/lofardata/templates/lofardata/index.html b/ldvspec/lofardata/templates/lofardata/index.html
index dd57bac1119189e47e3f8799e491b65b2154883e..18bb35f320c03058fba6f6fcf0f1cdb57434a907 100644
--- a/ldvspec/lofardata/templates/lofardata/index.html
+++ b/ldvspec/lofardata/templates/lofardata/index.html
@@ -1,35 +1,19 @@
-{% extends 'lofardata/../base.html' %}
+{% extends 'lofardata/base.html' %}
 {% load static %}
+{% load crispy_forms_tags %}
 
 {% block myBlock %}
 
-<div class="container-fluid details-container">
-
-    <table class="table table-striped table-bordered table-sm">
-
-        <tbody>
-
-            <tr><td>atdb_host</td>
-                <td><ul>
-                {%  for atdb_host in atdb_hosts %}
-                    <li>{{ atdb_host.name }} ( <a href="{{ atdb_host.url }}">{{ atdb_host.url }}</a> )</li>
-                {% endfor %}
-                </ul>
-                </td>
-            </tr>
-            <tr><td>api data</td><td><a href="{% url 'dataproduct' %}">{% url 'dataproduct' %}</a></td></tr>
-            <tr><td>api data-location</td><td><a href="{% url 'datalocation' %}">{% url 'datalocation' %}</a></td></tr>
-            <tr><td>api work-specification</td><td><a href="{% url 'workspecification' %}">{% url 'workspecification' %}</a></td></tr>
-
-            <tr><td>api-schema</td><td><a href="{% url 'openapi-schema' %}">{% url 'openapi-schema' %}</a></td></tr>
-        </tbody>
-
-    </table>
-    <p class="footer"> Version 1.0.0 (15 aug 2022 - 8:00)
-
-</div>
-
-
+    <div class="container-fluid">
+        <div class="row justify-content-md-center">
+            <div class="col-md-auto mt-5">Welcome to the LDV specification service</div>
+        </div>
+        {% if user.is_authenticated %}
+            <div class="row justify-content-md-center mt-5 mb-5">
+                <a class="col-md-2 btn btn-success" href="{% url 'specification' %}">Start specifying your task</a>
+            </div>
+        {% endif %}
+    </div>
 
 {% endblock %}
 
diff --git a/ldvspec/lofardata/templates/lofardata/specification.html b/ldvspec/lofardata/templates/lofardata/specification.html
new file mode 100644
index 0000000000000000000000000000000000000000..f9eced82e8bb8ebbea256c333b4d45b87b5b704c
--- /dev/null
+++ b/ldvspec/lofardata/templates/lofardata/specification.html
@@ -0,0 +1,294 @@
+{% extends 'lofardata/base.html' %}
+{% load static %}
+{% load crispy_forms_tags %}
+
+{% block myBlock %}
+    <div class="container-fluid details-container">
+        <div class="row">
+            <div class="col-12">
+                <div class="card">
+                    <div class="card-body">
+                        <div class="row">
+                            <div class="col-3">
+                                <div class="btn-group">
+                                    <button type="button" class="btn btn-primary btn-sm dropdown-toggle"
+                                            data-bs-toggle="dropdown" aria-expanded="false">
+                                        Select Specification
+                                    </button>
+                                    <ul class="dropdown-menu">
+                                        {{ specifications_list | safe }}
+                                    </ul>
+                                </div>
+                            </div>
+                            <div class="col-3">
+                                <a href="{% url 'specification' %}">
+                                    <div>
+                                        <button class="btn btn-primary btn-sm"><i class="fas fa-folder-plus"></i> New
+                                            Specification
+                                        </button>
+                                    </div>
+                                </a>
+                            </div>
+                        </div>
+                        <hr>
+                        <div class="row mb-5">
+                            <div class="col-1"><h4>ID {{ specification.id }} </h4></div>
+                            <div class="btn-group col-3">
+                                <button type="button" class="btn btn-info btn-sm dropdown-toggle"
+                                        data-bs-toggle="dropdown" aria-expanded="false">
+                                    ATDB tasks
+                                </button>
+                                <ul class="dropdown-menu">
+                                    {% for related_task in specification.related_tasks %}
+                                        <li>
+                                            <a class="dropdown-item"
+                                               href="{{ specification.processing_site.url }}tasks/{{ related_task }}">{{ related_task }}</a>
+                                        </li>
+                                    {% endfor %}
+                                </ul>
+                            </div>
+                            {% if specification.predecessor_specification %}
+                                <h4 class="col-4 predecessor_link">
+                                    Predecessor ID:
+
+                                    <a href="{% url 'specification-detail' specification.predecessor_specification.pk %}">
+                                        {{ specification.predecessor_specification.pk }}</a>
+
+                                </h4>
+                                <div class="btn-group col-3">
+                                    <button type="button" class="btn btn-info btn-sm dropdown-toggle"
+                                            data-bs-toggle="dropdown" aria-expanded="false">
+                                        Predecessor ATDB tasks
+                                    </button>
+                                    <ul class="dropdown-menu">
+                                        {% for related_task in specification.predecessor_specification.related_tasks %}
+                                            <li>
+                                                <a class="dropdown-item"
+                                                   href="{{ specification.predecessor_specification.processing_site.url }}tasks/{{ related_task }}">{{ related_task }}</a>
+                                            </li>
+                                        {% endfor %}
+                                    </ul>
+                                </div>
+                            {% endif %}
+
+                            {% if specification.successor.count %}
+
+                                <div class="btn-group col-3 ">
+                                    <button type="button" class="btn btn-info btn-sm dropdown-toggle"
+                                            data-bs-toggle="dropdown" aria-expanded="false">
+                                        Successors
+                                    </button>
+                                    <ul class="dropdown-menu">
+                                        {% for successor in specification.successor.all %}
+                                            <li>
+                                                <a class="dropdown-item"
+                                                   href="{% url 'specification-detail' successor.pk %}">Specification {{ successor.pk }}</a>
+                                            </li>
+                                        {% endfor %}
+                                    </ul>
+                                </div>
+                            {% endif %}
+                        </div>
+                        {% if specification.pk %}
+                            <form class="post-form" action="{% url 'specification-detail' specification.pk %}"
+                                  method="POST">
+                        {% else %}
+                            <form class="post-form" action="{% url 'specification' %}" method="POST">
+                        {% endif %}
+
+
+                        {% csrf_token %}
+                        <div>
+                            <div class="row">
+
+                                <div class="col-4">
+                                    <row>
+                                        {{ form.processing_site|as_crispy_field }}
+                                    </row>
+
+                                    <row>
+                                        {{ form.batch_size|as_crispy_field }}
+                                    </row>
+                                </div>
+
+                                <div class="col-4">
+                                    <label class="form-label" for="id_selected_workflow">Specified workflow
+                                        (ATDB)*</label>
+                                    <select id="id_selected_workflow" class="select form-select"
+                                            name="selected_workflow">
+                                        <option value="---">Empty</option>
+                                    </select>
+                                    <div class="row">{{ form.filters|as_crispy_field }}</div>
+                                </div>
+
+                                <div class="col-4">
+                                    <div class="row"><h3>Filters</h3></div>
+                                    {% for filter in filters %}
+                                        <div class="row">
+                                            <label class="form-label"
+                                                   for="id_{{ filter.field }}">{{ filter.name }}*</label>
+
+                                            {% if filter.filter_type == 'Free' %}
+                                                <input class="form-control custom-filter" type="text"
+                                                       id="id_{{ filter.field }}" name="{{ filter.field }}"
+                                                       data-filter="{{ filter.lookup_type }}"
+                                                       value="{{ filter.default }}">
+                                            {% elif filter.filter_type == 'Dropdown' %}
+                                                <select class="form-select select custom-filter"
+                                                        id="id_{{ filter.field }}" name="{{ filter.field }}"
+                                                        data-filter="{{ filter.lookup_type }}">
+                                                    {% for option in filter.choices %}
+                                                        {% if filter.default == option.0 %}
+                                                            <option selected
+                                                                    value="{{ option.0 }}">{{ option.0 }}</option>
+                                                        {% else %}
+                                                            <option value="{{ option.0 }}">{{ option.0 }}</option>
+                                                        {% endif %}
+                                                    {% endfor %}
+                                                </select>
+                                            {% else %}
+                                                <div class="warning">Not supported field</div>
+                                            {% endif %}
+
+                                        </div>
+                                    {% endfor %}
+
+                                </div>
+                            </div>
+
+                            {% if specification.filters is None %}
+                                <div class="row"><span
+                                        class="info-text text-center">Please specify one of the filters.</span></div>
+                            {% elif specification.is_ready == False %}
+                                <div class="row"><h4>Loading...</h4></div>
+                            {% else %}
+                                <label for="inputs_result"><h4>Inputs:</h4></label>
+                                {{ specification.inputs|json_script:"inputs" }}
+                                {{ form.inputs|as_crispy_field }}
+                            {% endif %}
+                            <div class="row bottom-bar">
+                                <div class="col-2">
+                                    <button class="btn btn-success btn-sm" type="submit" name="save" value="save"><i
+                                            class="fas fa-check"></i> Save and preview
+                                    </button>
+                                </div>
+                                <div class="col-2">
+                                    <button class="btn btn-success btn-sm" type="submit" name="save"
+                                            value="save_and_add_successor"><i class="fas fa-check"></i> Save and add
+                                        successor
+                                    </button>
+                                </div>
+                                <div class="col-3"><h5>Submission
+                                    status: {{ specification.get_submission_status_display }}</h5></div>
+                                <div class="col-2">
+                                    <button class="btn btn-success btn-sm" type="submit" name="save"
+                                            value="save_and_send_to_atdb"><i class="fas fa-check"></i> Save and send to
+                                        ATDB
+                                    </button>
+                                </div>
+
+                            </div>
+                        </form>
+                    </div>
+                </div>
+
+            </div>
+        </div>
+        <script>
+            const state = {
+                selected_workflow: "{{ specification.selected_workflow }}",
+                specification_id: "{{ specification.pk }}",
+                is_ready: "{{ specification.is_ready }}",
+                filters: "{{ specification.filters | safe }}"
+            };
+
+            function fetchAvailableWorkflows(ATDBurl) {
+                fetch(ATDBurl + 'workflows').then((response) => response.json()).then((data) => {
+                    $.each(data.results, function (key, value) {
+                        $('#id_selected_workflow').append($('<option>', {value: value.workflow_uri}).text(value.workflow_uri));
+                    });
+                }).then(() => {
+                    if (state.selected_workflow !== 'None') {
+                        $('#id_selected_workflow').val(state.selected_workflow)
+                    }
+                })
+            }
+
+            function delay(time) {
+                return new Promise(resolve => setTimeout(resolve, time));
+            }
+
+            function updateWorkflows(selectedProcessingSite) {
+                $('#id_selected_workflow').empty()
+                if (selectedProcessingSite === '') return
+                fetch('{% url 'processingsite-detail' 'replace' %}'.replace('replace', selectedProcessingSite)).then(
+                    (response) => response.json()).then(
+                    (data) => fetchAvailableWorkflows(data.url)
+                )
+            }
+
+            function selectedProcessingSite(event) {
+                let selectedProcessingSite = event.currentTarget.value
+                console.log('Processing site selected', event.currentTarget.value)
+                updateWorkflows(selectedProcessingSite);
+            }
+
+            function set_value_of_filters(item) {
+                const value = item.value;
+                const name = item.name;
+                const query_type = item.getAttribute('data-filter')
+                const join_sign = query_type === 'exact' ? '' : '_' + query_type
+                const filters_textbox = $('#id_filters')
+                const current_filters = filters_textbox.val() !== "null" ? JSON.parse(filters_textbox.val()) : {};
+                if (value === '' && current_filters[name + join_sign] !== undefined) {
+                    delete current_filters[name + join_sign]
+                } else {
+                    current_filters[name + join_sign] = value
+                }
+
+                filters_textbox.val(JSON.stringify(current_filters, undefined, 2))
+            }
+
+            function changeFilter(event) {
+                set_value_of_filters(event.currentTarget);
+            }
+
+            function attachSignals() {
+                $("#id_processing_site").on('change', selectedProcessingSite);
+
+                $(".custom-filter").on('input', changeFilter);
+                $("select.custom-filter").on('change', changeFilter);
+
+                console.log('current filters', state.filters)
+                if (state.filters === "None") {
+                    console.log('Still defining task', state);
+                } else if (state.is_ready === "True") {
+                    console.log('Results are ready', state);
+                } else {
+                    console.log('It should reload', state);
+                    delay(3000).then(() => location.reload(true));
+
+                }
+            }
+
+            function formatJSONField(field_name) {
+                try {
+                    const data = JSON.parse($(field_name).val());
+                    $(field_name).text(JSON.stringify(data, undefined, 2));
+                } catch (error) {
+
+                }
+            }
+
+            function onReady() {
+                attachSignals();
+                updateWorkflows($("#id_processing_site")[0].value);
+                formatJSONField('#id_inputs')
+                formatJSONField('#id_filters')
+            }
+
+            $(document).ready(onReady);
+        </script>
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/ldvspec/lofardata/templates/registration/login.html b/ldvspec/lofardata/templates/registration/login.html
index 6ed19e3c86dfabdbdda5e6edecf52f1ad4da5c55..ad2d056527ccb04598ef34f2d303da4ccb1161f7 100644
--- a/ldvspec/lofardata/templates/registration/login.html
+++ b/ldvspec/lofardata/templates/registration/login.html
@@ -1,4 +1,4 @@
-{% extends 'lofardata/../base.html' %}
+{% extends 'lofardata/base.html' %}
 
 {% load static %}
 
diff --git a/ldvspec/lofardata/urls.py b/ldvspec/lofardata/urls.py
index 2d4d4b2dac7b429ccdb35215d54dfdd9a1dd17df..c3d0b652f791390acb058086668d62ad5a90e8c9 100644
--- a/ldvspec/lofardata/urls.py
+++ b/ldvspec/lofardata/urls.py
@@ -1,17 +1,13 @@
-from django.contrib.auth import views as auth_views
 from django.urls import include, path
-from rest_framework.routers import DefaultRouter
+from django.contrib.auth import views as auth_views
 from rest_framework.schemas import get_schema_view
-
+from rest_framework.routers import DefaultRouter
 from . import views
 
 router = DefaultRouter()
-router.register(r'api/v1/workspecification', views.WorkSpecificationViewset, basename="workspecification")
-
+router.register(r'workspecification', views.WorkSpecificationViewset, basename="workspecification")
+router.register(r'processing_site', views.ATDBProcessingSiteView, basename='processingsite')
 urlpatterns = [
-    # Workaround for injecting the urls from the ModelViewSet, which requires a "Router"
-    *router.urls,
-
     # Perhaps both accounts and login could be moved to the ldv-spec main urls file?
     # authentication
     path('accounts/', include('django.contrib.auth.urls')),
@@ -31,6 +27,13 @@ urlpatterns = [
         description="API description",
         version="0.0.1"
     ), name='openapi-schema'),
+    path('api/v1/', include(router.urls)),
     # GUI
     path('', views.index, name='index'),
+    path('api/', views.api, name='api'),
+    path('specification/', views.specification_view, name='specification'),
+    path('specification/<int:pk>/', views.specification_view, name='specification-detail'),
+    # Workaround for injecting the urls from the ModelViewSet, which requires a "Router"
+
+
 ]
diff --git a/ldvspec/lofardata/views.py b/ldvspec/lofardata/views.py
index ad50d95a09e351722a2849f64e0b2da67e7cd281..2525a1bfbf09107cb7db39a34cc5ebbcc88b1412 100644
--- a/ldvspec/lofardata/views.py
+++ b/ldvspec/lofardata/views.py
@@ -1,25 +1,19 @@
-from django.conf import settings
 from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
+from django.shortcuts import redirect
 from django.shortcuts import render
+from django.urls import reverse
 from django_filters import rest_framework as filters
 from rest_framework import generics, status, viewsets
 from rest_framework.decorators import action
 from rest_framework.response import Response
 from rest_framework.schemas.openapi import AutoSchema
 
-from .models import (
-    ATDBProcessingSite,
-    DataLocation,
-    DataProduct,
-    DataProductFilter,
-    WorkSpecification,
-)
-from .serializers import (
-    DataLocationSerializer,
-    DataProductFlatSerializer,
-    DataProductSerializer,
-    WorkSpecificationSerializer,
-)
+from .forms import WorkSpecificationForm
+from .models import DataProduct, DataProductFilter, DataLocation, WorkSpecification, ATDBProcessingSite, DataFilterType
+from .serializers import DataProductSerializer, \
+    DataProductFlatSerializer, DataLocationSerializer, \
+    WorkSpecificationSerializer, ATDBProcessingSiteSerializer
 from .tasks import insert_task_into_atdb
 
 
@@ -33,7 +27,7 @@ class DynamicFilterSet(filters.FilterSet):
 
     def _load_filters(self):
         if self.Meta.filter_class is None:
-            raise Exception("Define filter_class meta attribute")
+            raise Exception('Define filter_class meta attribute')
         for item in self.Meta.filter_class.objects.all():
             field_obj = self.Meta.model._meta.get_field(item.field)
             filter_class, *_ = self.filter_for_lookup(field_obj, item.lookup_type)
@@ -58,6 +52,82 @@ def index(request):
     return render(request, "lofardata/index.html", {"atdb_hosts": atdb_hosts})
 
 
+def api(request):
+    atdb_hosts = ATDBProcessingSite.objects.values('name', 'url')
+    return render(request, "lofardata/api.html", {'atdb_hosts': atdb_hosts})
+
+
+def construct_specifications_list():
+    results = ''
+    for specification in WorkSpecification.objects.all():
+        url = reverse('specification-detail', args=(str(specification.pk),))
+        line = f'<li><a class="dropdown-item" href="{url}">{str(specification)}</a></li>'
+        results += line
+    return results
+
+
+def handle_specification_view_post(request, specification):
+    form = WorkSpecificationForm(request.POST, instance=specification)
+    if form.is_valid():
+        object_to_save = request.POST['save']
+
+        if object_to_save == 'save':
+            specification.async_task_result = None
+            specification.is_ready = False
+            specification.save()
+            return redirect('specification-detail', specification.pk)
+        elif object_to_save == 'save_and_send_to_atdb':
+            specification.save()
+            res = insert_task_into_atdb.delay(specification.pk)
+            return redirect('specification-detail', specification.pk)
+        elif object_to_save == 'save_and_add_successor':
+            specification.save()
+
+            successor = WorkSpecification()
+            successor.predecessor_specification = specification
+            successor.processing_site = specification.processing_site
+            successor.save()
+            return redirect('specification-detail', successor.pk)
+
+
+def preprocess_filters_specification_view(specification):
+    dataproduct_filters = DataProductFilter.objects.all()
+    for dataproduct_filter in dataproduct_filters:
+        if specification.filters and dataproduct_filter.field in specification.filters:
+            dataproduct_filter.default = specification.filters[dataproduct_filter.field]
+        else:
+            dataproduct_filter.default = ''
+
+        if dataproduct_filter.filter_type == DataFilterType.DROPDOWN:
+            dataproduct_filter.choices = DataProduct.objects.distinct(dataproduct_filter.field).values_list(
+                dataproduct_filter.field)
+    return dataproduct_filters
+
+
+def specification_view(request, pk=None):
+    try:
+        # retrieve existing specification
+        specification = WorkSpecification.objects.get(pk=pk)
+    except ObjectDoesNotExist:
+        # start new specification
+        specification = WorkSpecification()
+
+    specifications_list = construct_specifications_list()
+    # a POST means that the form is filled in and should be stored in the
+    form = WorkSpecificationForm(instance=specification)
+    if request.method == "POST":
+        return handle_specification_view_post(request, specification)
+
+    dataproduct_filters = preprocess_filters_specification_view(specification)
+
+    return render(request, "lofardata/specification.html", {
+        'form': form,
+        'specification': specification,
+        'specifications_list': specifications_list,
+        'filters': dataproduct_filters
+    })
+
+
 # ---------- REST API views ----------
 class DataProductView(generics.ListCreateAPIView):
     model = DataProduct
@@ -70,6 +140,13 @@ class DataProductView(generics.ListCreateAPIView):
     filter_class = DataProductFilterSet
 
 
+class ATDBProcessingSiteView(viewsets.ReadOnlyModelViewSet):
+    model = ATDBProcessingSite
+    serializer_class = ATDBProcessingSiteSerializer
+
+    queryset = ATDBProcessingSite.objects.all().order_by('pk')
+
+
 class DataProductDetailsView(generics.RetrieveUpdateDestroyAPIView):
     model = DataProduct
     serializer_class = DataProductSerializer
@@ -116,11 +193,7 @@ class WorkSpecificationViewset(viewsets.ModelViewSet):
 
     @action(detail=True, methods=["POST"])
     def submit(self, request, pk=None) -> Response:
-
         # TODO: check that there are some matches in the request?
-
         # TODO: how to specify the filter?
-
         res = insert_task_into_atdb.delay(pk)
-
         return Response({"detail": "accepted"}, status=status.HTTP_202_ACCEPTED)
diff --git a/ldvspec/makemigrations.bat b/ldvspec/makemigrations.bat
new file mode 100644
index 0000000000000000000000000000000000000000..c5a3687057ac7d0650547c3e3804e71739877652
--- /dev/null
+++ b/ldvspec/makemigrations.bat
@@ -0,0 +1 @@
+python manage.py makemigrations --settings=ldvspec.settings.dev
diff --git a/ldvspec/migrate.bat b/ldvspec/migrate.bat
new file mode 100644
index 0000000000000000000000000000000000000000..92fcbcaa44b7fe0a77bb1c98309fd8e7bd1ebede
--- /dev/null
+++ b/ldvspec/migrate.bat
@@ -0,0 +1 @@
+python manage.py migrate --settings=ldvspec.settings.dev
diff --git a/ldvspec/requirements/base.txt b/ldvspec/requirements/base.txt
index 3276dfaea43d7450f8fe414dbe87b5561e9b8754..7fefc133b803df987ff6a7bce0e5673ea7e230f9 100644
--- a/ldvspec/requirements/base.txt
+++ b/ldvspec/requirements/base.txt
@@ -13,3 +13,5 @@ pyyaml==6.0
 uritemplate==4.1.1
 sshtunnel==0.4.0
 django-uws==0.2.dev355575
+django-crispy-forms==1.14.0
+ crispy-bootstrap5==0.6
\ No newline at end of file
diff --git a/ldvspec/run.bat b/ldvspec/run.bat
new file mode 100644
index 0000000000000000000000000000000000000000..4152923cacb7501bd2dff211e75aca2d77fb602c
--- /dev/null
+++ b/ldvspec/run.bat
@@ -0,0 +1,2 @@
+SET CELERY_BROKER_URL=amqp://guest@raspiastro:5672/
+python manage.py runserver --settings=ldvspec.settings.dev
diff --git a/ldvspec/test.bat b/ldvspec/test.bat
new file mode 100644
index 0000000000000000000000000000000000000000..fad51aaa0e1e09d34f3a705f40bc6c546d9dbb75
--- /dev/null
+++ b/ldvspec/test.bat
@@ -0,0 +1 @@
+python manage.py test --settings=ldvspec.settings.dev
diff --git a/ldvspec/workers.bat b/ldvspec/workers.bat
new file mode 100644
index 0000000000000000000000000000000000000000..41ce73ce2cdae3d6ef862eb839eb5840fa3044ce
--- /dev/null
+++ b/ldvspec/workers.bat
@@ -0,0 +1,2 @@
+SET CELERY_BROKER_URL=amqp://guest@raspiastro:5672
+celery -A ldvspec worker --pool=solo -l INFO