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

Merge branch 'gui-update' into 'main'

Gui update

See merge request !19
parents fbcd4730 48ab13fd
No related branches found
No related tags found
1 merge request!19Gui update
Pipeline #39296 passed with warnings
Showing
with 4163 additions and 267 deletions
...@@ -93,8 +93,9 @@ integration-test: ...@@ -93,8 +93,9 @@ integration-test:
- cd integration - cd integration
# TODO: cache the integration image # TODO: cache the integration image
- docker compose build - docker compose build
- docker compose up -d atdb-backend ldv-specification-backend && sleep 30 - docker compose up -d atdb-backend ldv-specification-backend
- docker compose run integration - docker compose run integration
allow_failure: true # TODO: fix integration test
......
...@@ -16,5 +16,10 @@ services: ...@@ -16,5 +16,10 @@ services:
- ldv-spec-db:/var/lib/postgresql/data - ldv-spec-db:/var/lib/postgresql/data
restart: always restart: always
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
volumes: volumes:
ldv-spec-db: ldv-spec-db:
...@@ -40,6 +40,7 @@ INSTALLED_APPS = [ ...@@ -40,6 +40,7 @@ INSTALLED_APPS = [
'uws', 'uws',
'crispy_forms', 'crispy_forms',
'crispy_bootstrap5', 'crispy_bootstrap5',
'widget_tweaks',
] ]
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
...@@ -149,4 +150,3 @@ else: ...@@ -149,4 +150,3 @@ else:
SESSION_COOKIE_NAME = 'ldv-spec-sessionid' SESSION_COOKIE_NAME = 'ldv-spec-sessionid'
CSRF_COOKIE_NAME = 'ldv-spec-csrftoken' CSRF_COOKIE_NAME = 'ldv-spec-csrftoken'
...@@ -10,11 +10,11 @@ CORS_ORIGIN_ALLOW_ALL = True ...@@ -10,11 +10,11 @@ CORS_ORIGIN_ALLOW_ALL = True
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2', 'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'atdb_admin', 'USER': 'postgres',
'PASSWORD': 'atdb123', 'PASSWORD': 'secret',
'NAME': 'ldv-spec-db', 'NAME': 'ldv-spec-db',
'HOST': 'localhost', 'HOST': 'localhost',
'PORT': '5432', 'PORT': '5433',
}, },
} }
......
from django import forms from .models import WorkSpecification, DataProductFilter
from django.forms import ModelForm from django.forms import ModelForm
from .models import WorkSpecification
class WorkSpecificationForm(ModelForm): class WorkSpecificationForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['inputs'].required = False self.fields['predecessor_specification'].required = False
self.fields['filters'].required = False
def _extract_filters(self):
filter_names = [filter_name[0] for filter_name in DataProductFilter.objects.all().values_list('field')]
filters = {}
for filter_name in filter_names:
if filter_name in self.data:
filters[filter_name] = self.data[filter_name]
return filters
def clean(self):
self.cleaned_data = super().clean()
self.cleaned_data["filters"] = self._extract_filters()
return self.cleaned_data
class Meta: class Meta:
model = WorkSpecification model = WorkSpecification
fields = ['filters', 'selected_workflow', 'processing_site', 'inputs', 'batch_size'] fields = ['filters', 'selected_workflow', 'processing_site', 'predecessor_specification', 'batch_size']
labels = { labels = {
'filters': 'Filters',
'selected_workflow': 'Selected workflow', 'selected_workflow': 'Selected workflow',
'processing_site': 'Processing Site (ATDB)', 'processing_site': 'Processing Site (ATDB)',
'inputs': 'Inputs', 'predecessor_specification': 'Predecessor work specification',
'batch_size': '# of files per ATDB tasks' 'batch_size': 'Files per task'
} }
# Generated by Django 3.2 on 2022-11-10 13:07
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('lofardata', '0008_update_models_to_deal_with_gui'),
]
operations = [
migrations.RemoveField(
model_name='workspecification',
name='predecessor_task',
),
migrations.AlterField(
model_name='workspecification',
name='predecessor_specification',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='successor', to='lofardata.workspecification'),
),
]
...@@ -5,6 +5,7 @@ from django.contrib.auth.models import User ...@@ -5,6 +5,7 @@ from django.contrib.auth.models import User
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_filters.rest_framework import FilterSet
class DataLocation(models.Model): class DataLocation(models.Model):
...@@ -139,9 +140,8 @@ class WorkSpecification(models.Model): ...@@ -139,9 +140,8 @@ class WorkSpecification(models.Model):
# Task ID's that were created in ATDB # Task ID's that were created in ATDB
related_tasks = ArrayField(models.IntegerField(), null=True) 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, predecessor_specification = models.ForeignKey('self', null=True, on_delete=models.DO_NOTHING,
related_name='successor') related_name='successor', blank=True)
# The query for gathering files has been executed # The query for gathering files has been executed
is_ready = models.BooleanField(default=False) is_ready = models.BooleanField(default=False)
......
ldvspec/lofardata/static/favicon.ico

1.06 KiB

TD {
font-family: Raleway, serif;
font-size: 12pt;
}
.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;
}
.active {
background-color: lemonchiffon;
}
.max {
font-weight: bold;
background-color: lightgreen;
}
.aggregate {
font-weight: bold;
background-color: lightgreen;
}
.aggregate_failed {
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;
}
.processed, .ok, .running {
color: green;
font-weight: bold;
}
.scrubbed {
color: darkgray;
font-style: italic;
}
.processing, .processing_copying {
font-weight: bold;
background-color: lightyellow;
}
p.title {
font-family: Raleway, serif;
font-size: 18pt;
}
.footer {
font-family: Arial, serif;
font-size: 10pt;
font-style: italic;
}
.ml-auto .dropdown-menu {
left: auto !important;
right: 0;
}
.bigger {
font-size: 13pt;
}
.info {
background-color: #E0F8F8;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
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;
}
.modal-body {
padding: 0 !important;
}
.modal-dialog .modal-lg {
max-width: 75% !important;
}
.modal-content {
overflow: scroll !important;
}
.form-flex {
display: flex;
}
.page {
position: relative;
min-height: 100vh;
}
.content-wrap {
padding-bottom: 3.5rem; /* Footer height */
}
footer {
text-align: center;
padding: .4rem;
vertical-align: center;
display: block;
position: absolute;
margin-top: auto !important;
margin-bottom: 0;
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
This diff is collapsed.
File added
This diff is collapsed.
File added
File added
ldvspec/lofardata/static/lofardata/styling/images/ldvspec_logo_2.png

479 KiB

<svg version="1.1"
class="svg-loader"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 80 80"
xml:space="preserve"
>
<path
fill="#262626"
d="M10,40c0,0,0-0.4,0-1.1c0-0.3,0-0.8,0-1.3c0-0.3,0-0.5,0-0.8c0-0.3,0.1-0.6,0.1-0.9c0.1-0.6,0.1-1.4,0.2-2.1
c0.2-0.8,0.3-1.6,0.5-2.5c0.2-0.9,0.6-1.8,0.8-2.8c0.3-1,0.8-1.9,1.2-3c0.5-1,1.1-2,1.7-3.1c0.7-1,1.4-2.1,2.2-3.1
c1.6-2.1,3.7-3.9,6-5.6c2.3-1.7,5-3,7.9-4.1c0.7-0.2,1.5-0.4,2.2-0.7c0.7-0.3,1.5-0.3,2.3-0.5c0.8-0.2,1.5-0.3,2.3-0.4l1.2-0.1
l0.6-0.1l0.3,0l0.1,0l0.1,0l0,0c0.1,0-0.1,0,0.1,0c1.5,0,2.9-0.1,4.5,0.2c0.8,0.1,1.6,0.1,2.4,0.3c0.8,0.2,1.5,0.3,2.3,0.5
c3,0.8,5.9,2,8.5,3.6c2.6,1.6,4.9,3.4,6.8,5.4c1,1,1.8,2.1,2.7,3.1c0.8,1.1,1.5,2.1,2.1,3.2c0.6,1.1,1.2,2.1,1.6,3.1
c0.4,1,0.9,2,1.2,3c0.3,1,0.6,1.9,0.8,2.7c0.2,0.9,0.3,1.6,0.5,2.4c0.1,0.4,0.1,0.7,0.2,1c0,0.3,0.1,0.6,0.1,0.9
c0.1,0.6,0.1,1,0.1,1.4C74,39.6,74,40,74,40c0.2,2.2-1.5,4.1-3.7,4.3s-4.1-1.5-4.3-3.7c0-0.1,0-0.2,0-0.3l0-0.4c0,0,0-0.3,0-0.9
c0-0.3,0-0.7,0-1.1c0-0.2,0-0.5,0-0.7c0-0.2-0.1-0.5-0.1-0.8c-0.1-0.6-0.1-1.2-0.2-1.9c-0.1-0.7-0.3-1.4-0.4-2.2
c-0.2-0.8-0.5-1.6-0.7-2.4c-0.3-0.8-0.7-1.7-1.1-2.6c-0.5-0.9-0.9-1.8-1.5-2.7c-0.6-0.9-1.2-1.8-1.9-2.7c-1.4-1.8-3.2-3.4-5.2-4.9
c-2-1.5-4.4-2.7-6.9-3.6c-0.6-0.2-1.3-0.4-1.9-0.6c-0.7-0.2-1.3-0.3-1.9-0.4c-1.2-0.3-2.8-0.4-4.2-0.5l-2,0c-0.7,0-1.4,0.1-2.1,0.1
c-0.7,0.1-1.4,0.1-2,0.3c-0.7,0.1-1.3,0.3-2,0.4c-2.6,0.7-5.2,1.7-7.5,3.1c-2.2,1.4-4.3,2.9-6,4.7c-0.9,0.8-1.6,1.8-2.4,2.7
c-0.7,0.9-1.3,1.9-1.9,2.8c-0.5,1-1,1.9-1.4,2.8c-0.4,0.9-0.8,1.8-1,2.6c-0.3,0.9-0.5,1.6-0.7,2.4c-0.2,0.7-0.3,1.4-0.4,2.1
c-0.1,0.3-0.1,0.6-0.2,0.9c0,0.3-0.1,0.6-0.1,0.8c0,0.5-0.1,0.9-0.1,1.3C10,39.6,10,40,10,40z"
>
<animateTransform
attributeType="xml"
attributeName="transform"
type="rotate"
from="0 40 40"
to="360 40 40"
dur="0.8s"
repeatCount="indefinite"
/>
</path>
<path
fill="#00adee"
d="M62,40.1c0,0,0,0.2-0.1,0.7c0,0.2,0,0.5-0.1,0.8c0,0.2,0,0.3,0,0.5c0,0.2-0.1,0.4-0.1,0.7
c-0.1,0.5-0.2,1-0.3,1.6c-0.2,0.5-0.3,1.1-0.5,1.8c-0.2,0.6-0.5,1.3-0.7,1.9c-0.3,0.7-0.7,1.3-1,2.1c-0.4,0.7-0.9,1.4-1.4,2.1
c-0.5,0.7-1.1,1.4-1.7,2c-1.2,1.3-2.7,2.5-4.4,3.6c-1.7,1-3.6,1.8-5.5,2.4c-2,0.5-4,0.7-6.2,0.7c-1.9-0.1-4.1-0.4-6-1.1
c-1.9-0.7-3.7-1.5-5.2-2.6c-1.5-1.1-2.9-2.3-4-3.7c-0.6-0.6-1-1.4-1.5-2c-0.4-0.7-0.8-1.4-1.2-2c-0.3-0.7-0.6-1.3-0.8-2
c-0.2-0.6-0.4-1.2-0.6-1.8c-0.1-0.6-0.3-1.1-0.4-1.6c-0.1-0.5-0.1-1-0.2-1.4c-0.1-0.9-0.1-1.5-0.1-2c0-0.5,0-0.7,0-0.7
s0,0.2,0.1,0.7c0.1,0.5,0,1.1,0.2,2c0.1,0.4,0.2,0.9,0.3,1.4c0.1,0.5,0.3,1,0.5,1.6c0.2,0.6,0.4,1.1,0.7,1.8
c0.3,0.6,0.6,1.2,0.9,1.9c0.4,0.6,0.8,1.3,1.2,1.9c0.5,0.6,1,1.3,1.6,1.8c1.1,1.2,2.5,2.3,4,3.2c1.5,0.9,3.2,1.6,5,2.1
c1.8,0.5,3.6,0.6,5.6,0.6c1.8-0.1,3.7-0.4,5.4-1c1.7-0.6,3.3-1.4,4.7-2.4c1.4-1,2.6-2.1,3.6-3.3c0.5-0.6,0.9-1.2,1.3-1.8
c0.4-0.6,0.7-1.2,1-1.8c0.3-0.6,0.6-1.2,0.8-1.8c0.2-0.6,0.4-1.1,0.5-1.7c0.1-0.5,0.2-1,0.3-1.5c0.1-0.4,0.1-0.8,0.1-1.2
c0-0.2,0-0.4,0.1-0.5c0-0.2,0-0.4,0-0.5c0-0.3,0-0.6,0-0.8c0-0.5,0-0.7,0-0.7c0-1.1,0.9-2,2-2s2,0.9,2,2C62,40,62,40.1,62,40.1z"
>
<animateTransform
attributeType="xml"
attributeName="transform"
type="rotate"
from="0 40 40"
to="-360 40 40"
dur="0.6s"
repeatCount="indefinite"
/>
</path>
</svg>
\ No newline at end of file
function fetchAvailableWorkflows(ATDBurl) {
fetch(ATDBurl + 'workflows').then((response) => response.json()).then((data) => {
$.each(data.results, function (key, value) {
let workflowDropdownOption = $('<option>', {value: value.workflow_uri}).text(value.workflow_uri);
$('#id_selected_workflow').append(workflowDropdownOption);
});
}).then(() => {
$('#id_selected_workflow').val($("#id_selected_workflow option:first").val());
})
}
function updateWorkflows(selectedProcessingSite) {
$('#id_selected_workflow').empty()
if (selectedProcessingSite === '') return
let processingSiteUrl = processingSite.replace('replace', selectedProcessingSite);
fetch(processingSiteUrl).then(
(response) => response.json()).then(
(data) => fetchAvailableWorkflows(data.url)
)
}
function selectedProcessingSite(event) {
let site = event.currentTarget.value
updateWorkflows(site);
}
function onReady() {
let processingSite = $("#id_processing_site"); //#TODO: fix proc site initial on update
let initialProcessingSite = processingSite[0].value;
if (initialProcessingSite) {
updateWorkflows(initialProcessingSite)
}
processingSite.on('change', selectedProcessingSite);
}
$(document).ready(onReady);
\ No newline at end of file
import re import re
from typing import Any, List, Optional, Dict from typing import Any, Dict, List, Optional
from urllib.parse import urlparse from urllib.parse import urlparse
import requests import requests
from requests.auth import AuthBase from celery.utils.log import get_task_logger
from requests.exceptions import RequestException
from ldvspec.celery import app
from lofardata.models import ( from lofardata.models import (
SUBMISSION_STATUS, SUBMISSION_STATUS,
ATDBProcessingSite, ATDBProcessingSite,
DataProduct, DataProduct,
WorkSpecification, WorkSpecification,
) )
from requests.auth import AuthBase
from requests.exceptions import RequestException
from ldvspec.celery import app
logger = get_task_logger(__name__)
class RequestNotOk(Exception): class RequestNotOk(Exception):
...@@ -22,6 +25,8 @@ class RequestNotOk(Exception): ...@@ -22,6 +25,8 @@ class RequestNotOk(Exception):
class WorkSpecificationNoSite(Exception): class WorkSpecificationNoSite(Exception):
pass pass
class InvalidPredecessor(ValueError):
pass
class SessionStore: class SessionStore:
"""requests.Session Singleton""" """requests.Session Singleton"""
...@@ -40,7 +45,7 @@ def define_work_specification(workspecification_id): ...@@ -40,7 +45,7 @@ def define_work_specification(workspecification_id):
specification = WorkSpecification.objects.get(pk=workspecification_id) specification = WorkSpecification.objects.get(pk=workspecification_id)
filters = specification.filters filters = specification.filters
dataproducts = DataProduct.objects.filter(**filters).order_by('surl') dataproducts = DataProduct.objects.filter(**filters).order_by("surl")
inputs = { inputs = {
"surls": [ "surls": [
{"surl": dataproduct.surl, "size": dataproduct.filesize} {"surl": dataproduct.surl, "size": dataproduct.filesize}
...@@ -71,7 +76,7 @@ def _prepare_request_payload( ...@@ -71,7 +76,7 @@ def _prepare_request_payload(
workflow_url: str, workflow_url: str,
purge_policy: str = "no", purge_policy: str = "no",
predecessor: int = None, predecessor: int = None,
optional_parameters: Dict[str, Any] = None optional_parameters: Dict[str, Any] = None,
): ):
# Parse a single surl for info: # Parse a single surl for info:
# project, sas_id & location # project, sas_id & location
...@@ -90,10 +95,7 @@ def _prepare_request_payload( ...@@ -90,10 +95,7 @@ def _prepare_request_payload(
for e in entries for e in entries
] ]
if optional_parameters: if optional_parameters:
inputs = { inputs = {**optional_parameters, "surls": inputs}
**optional_parameters,
'surls': inputs
}
data = { data = {
"project": project_id, "project": project_id,
...@@ -154,7 +156,7 @@ def insert_task_into_atdb(workspecification_id: int): ...@@ -154,7 +156,7 @@ def insert_task_into_atdb(workspecification_id: int):
pk=workspecification_id pk=workspecification_id
) )
inputs: Dict[str, Any] = work_spec.inputs.copy() inputs: Dict[str, Any] = work_spec.inputs.copy()
entries: List[dict] = inputs.pop('surls') entries: List[dict] = inputs.pop("surls")
batches = split_entries_to_batches(entries, work_spec.batch_size) batches = split_entries_to_batches(entries, work_spec.batch_size)
...@@ -163,6 +165,16 @@ def insert_task_into_atdb(workspecification_id: int): ...@@ -163,6 +165,16 @@ def insert_task_into_atdb(workspecification_id: int):
raise WorkSpecificationNoSite() raise WorkSpecificationNoSite()
url = site.url + "tasks/" url = site.url + "tasks/"
# Task ID of the predecessor
atdb_predecessor_task_id: int | None = None
if work_spec.predecessor_specification is not None:
predecessor: WorkSpecification = work_spec.predecessor_specification
if len(predecessor.related_tasks != 1):
logger.error("Workspecification {} has no valid predecessor".format(work_spec.pk))
raise InvalidPredecessor()
# Should only be 1 entry
atdb_predecessor_task_id = predecessor.related_tasks[0]
try: try:
for batch in batches: for batch in batches:
payload = _prepare_request_payload( payload = _prepare_request_payload(
...@@ -171,7 +183,7 @@ def insert_task_into_atdb(workspecification_id: int): ...@@ -171,7 +183,7 @@ def insert_task_into_atdb(workspecification_id: int):
filter_id=f"ldv-spec:{work_spec.pk}", filter_id=f"ldv-spec:{work_spec.pk}",
workflow_url=work_spec.selected_workflow, workflow_url=work_spec.selected_workflow,
purge_policy=work_spec.purge_policy, purge_policy=work_spec.purge_policy,
predecessor=work_spec.predecessor_task, predecessor=atdb_predecessor_task_id,
) )
res = sess.post(url, json=payload, auth=TokenAuth(site.access_token)) res = sess.post(url, json=payload, auth=TokenAuth(site.access_token))
......
...@@ -13,17 +13,11 @@ ...@@ -13,17 +13,11 @@
<!-- loads the path to static files --> <!-- loads the path to static files -->
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'> <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" <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css"
integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous"> 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> <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="stylesheet" type="text/css" href="{% static 'lofardata/styling/dias.css' %}">
<link rel="icon" href="{% static 'favicon.ico' %}">
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}
...@@ -31,40 +25,44 @@ ...@@ -31,40 +25,44 @@
</head> </head>
<body> <body>
<div class="page"> <div class="page">
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <nav class="h-navbar navbar-custom">
<div class="container-fluid"> <ol class="h-navbar-list">
<ul class="nav navbar-nav"> <li class="h-navbar-list__item">
<!-- Header --> <a href="{% url 'index' %}">
<li><a class="navbar-brand" href="{% url 'index' %}"> <img class="h-navbar-logo logo"
<img src="{% static 'lofardata/ldvspec_logo.png' %}" height="30" alt=""> src="{% static 'lofardata/styling/images/ldvspec_logo_2.png' %}" alt=""/>
&nbsp;Specification Service</a> </a>
</li>
{% if user.is_authenticated and user.is_staff %}
<li class="h-navbar-list__item">
<a class="button text--primary"
href="{% url 'admin:index' %}">
Admin Page
</a>
</li> </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 %} {% endif %}
<li><a class="nav-link" href="{% url 'api' %}">API</a></li> </ol>
<li><a class="nav-link" href="{% url 'admin:index' %}" target="_blank">Admin Page</a></li> <ul class="h-navbar-list margin-right">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a class="nav-link" href="{% url 'logout' %}" target="_blank">Logout ({{ user.get_username }})</a> <li class="h-navbar-list__item">
{% endif %} Logged in as:<p class="text text--semi-bold margin-left"> {{ user.get_username }}</p>
{% if not user.is_authenticated %} </li>
<a class="nav-link" href="{% url 'login' %}" target="_blank">Login</a> <li class="h-navbar-list__item">
<a class="button button--secondary margin-right" href="{% url 'logout' %}">Logout</a>
</li>
{% else %}
<li class="h-navbar-list__item">
<a class="button button--secondary margin-right" href="{% url 'login' %}">Login</a>
</li>
{% endif %} {% endif %}
</ul> </ul>
</div>
</nav> </nav>
<div class="content-wrap"> <div class="content-wrap">
<!-- to add blocks of code --> <!-- to add blocks of code -->
{% block myBlock %} {% block myBlock %}
{% endblock %} {% endblock %}
</div> </div>
<footer>
<p> {{ VERSION_STRING }} </p>
</footer>
</div> </div>
</body> </body>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment