Skip to content
Snippets Groups Projects
views.py 11.3 KiB
Newer Older
Fanna Lautenbach's avatar
Fanna Lautenbach committed
import time

from django.contrib.auth.mixins import LoginRequiredMixin
Mattia Mancini's avatar
Mattia Mancini committed
from django.contrib.auth.models import User
Nico Vermaas's avatar
Nico Vermaas committed
from django.core.exceptions import ObjectDoesNotExist
Fanna Lautenbach's avatar
Fanna Lautenbach committed
from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render
from django.views.generic import DeleteView, DetailView, UpdateView, TemplateView
Fanna Lautenbach's avatar
Fanna Lautenbach committed
from django.views.generic.list import ListView
Nico Vermaas's avatar
Nico Vermaas committed
from django_filters import rest_framework as filters
from rest_framework import generics, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
Fanna Lautenbach's avatar
Fanna Lautenbach committed
from rest_framework.reverse import reverse_lazy
from rest_framework.schemas.openapi import AutoSchema
Nico Vermaas's avatar
Nico Vermaas committed

from .forms import WorkSpecificationForm, GroupForm
from .mixins import CanAccessWorkSpecificationMixin
Fanna Lautenbach's avatar
Fanna Lautenbach committed
from .models import (
    ATDBProcessingSite,
    DataProduct,
    DataProductFilter,
    WorkSpecification,
Fanna Lautenbach's avatar
Fanna Lautenbach committed
)
from .serializers import (
    ATDBProcessingSiteSerializer,
    DataProductFlatSerializer,
    DataProductSerializer,
    WorkSpecificationSerializer,
)
from .tasks import insert_task_into_atdb
from .view_helpers import inputs_processor, dataproductinfo, filters_preprocessor
from .view_helpers.specification import set_post_submit_values, split_obs_ids_string, \
    create_work_specifications_for_group
Mattia Mancini's avatar
Mattia Mancini committed
class DynamicFilterSet(filters.FilterSet):
Nico Vermaas's avatar
Nico Vermaas committed
    class Meta:
Mattia Mancini's avatar
Mattia Mancini committed
        filter_class = None

    def __init__(self, *args, **kwargs):
        self._load_filters()
        super().__init__(*args, **kwargs)

    def _load_filters(self):
        if self.Meta.filter_class is None:
Fanna Lautenbach's avatar
Fanna Lautenbach committed
            raise Exception("Define filter_class meta attribute")
Mattia Mancini's avatar
Mattia Mancini committed
        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)
            self.base_filters[item.name] = filter_class(item.field)
Nico Vermaas's avatar
Nico Vermaas committed

Mattia Mancini's avatar
Mattia Mancini committed

# --- Filters ---
class DataProductFilterSet(DynamicFilterSet):
    class Meta:
        model = DataProduct
        filter_class = DataProductFilter
Nico Vermaas's avatar
Nico Vermaas committed
        fields = {
            "obs_id": ["exact", "icontains"],
Nico Vermaas's avatar
Nico Vermaas committed
        }

Mattia Mancini's avatar
Mattia Mancini committed

# ---------- GUI Views -----------
Nico Vermaas's avatar
Nico Vermaas committed

Fanna Lautenbach's avatar
Fanna Lautenbach committed
def api(request):
    atdb_hosts = ATDBProcessingSite.objects.values("name", "url")
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    return render(request, "lofardata/api.html", {"atdb_hosts": atdb_hosts})
Nico Vermaas's avatar
Nico Vermaas committed

Fanna Lautenbach's avatar
Fanna Lautenbach committed
class Specifications(ListView):
    serializer_class = WorkSpecificationSerializer
    template_name = "lofardata/index.html"
    model = WorkSpecification
    ordering = ["-created_on"]

    def get_queryset(self):
        queryset = WorkSpecification.objects.all()
        current_user: User = self.request.user
        if current_user.is_staff or current_user.is_superuser:
            return queryset.order_by("-created_on")
        return queryset.filter(created_by=current_user.id).order_by("-created_on")
class WorkSpecificationCreateUpdateView(LoginRequiredMixin, CanAccessWorkSpecificationMixin, UpdateView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/create_update.html"
    model = WorkSpecification
    form_class = WorkSpecificationForm
    def get(self, request, *args, **kwargs):
        specification = self.get_object()
        if specification.is_editable():
            return super().get(request, *args, **kwargs)
        else:
            return redirect('specification-detail', self.kwargs["pk"])

Fanna Lautenbach's avatar
Fanna Lautenbach committed
    def get_object(self, queryset=None):
        if self.kwargs.__len__() == 0 or self.kwargs["pk"] is None:
            specification = WorkSpecification()
        else:
            specification = WorkSpecification.objects.get(pk=self.kwargs["pk"])
        return specification

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        try:
            specification = WorkSpecification.objects.get(pk=context["object"].pk)
        except ObjectDoesNotExist:
            specification = None
        context["filters"] = filters_preprocessor.preprocess(specification)
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        context["processing_sites"] = list(ATDBProcessingSite.objects.values("name", "url"))
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        return context

    def create_successor(self, specification):
        successor = WorkSpecification()
        successor.predecessor_specification = specification
        successor.processing_site = specification.processing_site
        successor.save()
        return self.get_success_url(pk=successor.pk)

    def form_valid(self, form):
        action_ = form.data["action"]
        specification = form.instance
        if action_ == "Submit":
            set_post_submit_values(specification, self.request.user)
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        if action_ == "Send":
            insert_task_into_atdb.delay(specification.pk)
        if action_ == "Successor":
Nico Vermaas's avatar
Nico Vermaas committed
            specification.save()
            successor = WorkSpecification()
            successor.predecessor_specification = specification
            successor.processing_site = specification.processing_site
Fanna Lautenbach's avatar
Fanna Lautenbach committed
            successor.selected_workflow = specification.selected_workflow
            return HttpResponseRedirect(self.create_successor(specification))
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        return super().form_valid(form)
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    def get_success_url(self, **kwargs):
        if kwargs.__len__() == 0 or kwargs["pk"] is None:
            return reverse_lazy("index")
Nico Vermaas's avatar
Nico Vermaas committed
        else:
Fanna Lautenbach's avatar
Fanna Lautenbach committed
            return reverse_lazy("specification-update", kwargs={"pk": kwargs["pk"]})
Fanna Lautenbach's avatar
Fanna Lautenbach committed

class WorkSpecificationDetailView(LoginRequiredMixin, CanAccessWorkSpecificationMixin, DetailView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/detail.html"
    model = WorkSpecification

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        specification = WorkSpecification.objects.get(pk=context["object"].pk)
        context["filters"] = filters_preprocessor.preprocess(specification)
        total_input_size, number_of_files, average_file_size = inputs_processor.compute_size_of_inputs(
Fanna Lautenbach's avatar
Fanna Lautenbach committed
            specification.inputs
        )
        context["number_of_files"] = number_of_files
        context["total_input_size"] = inputs_processor.format_size(total_input_size)
        context["size_per_task"] = inputs_processor.format_size(
Fanna Lautenbach's avatar
Fanna Lautenbach committed
            average_file_size * specification.batch_size
            if specification.batch_size > 0
            else total_input_size
        )
        return context


class WorkSpecificationDeleteView(LoginRequiredMixin, CanAccessWorkSpecificationMixin, DeleteView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/delete.html"
    model = WorkSpecification
    success_url = reverse_lazy("index")
class WorkSpecificationInputsView(LoginRequiredMixin, CanAccessWorkSpecificationMixin, DetailView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/inputs.html"
    model = WorkSpecification


class WorkSpecificationATDBTasksView(LoginRequiredMixin, CanAccessWorkSpecificationMixin, DetailView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/tasks.html"
    model = WorkSpecification
class WorkSpecificationDatasetSizeInfoView(LoginRequiredMixin, CanAccessWorkSpecificationMixin, DetailView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/dataset_size_info.html"
    model = WorkSpecification

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        specification = WorkSpecification.objects.get(pk=context["object"].pk)

        min_size, max_size, n_bins, counts, biggest_bucket, bins = inputs_processor.compute_inputs_histogram(
            specification.inputs)
Fanna Lautenbach's avatar
Fanna Lautenbach committed

        context["min_size"] = inputs_processor.format_size(min_size)
        context["max_size"] = inputs_processor.format_size(max_size)
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        context["biggest_bucket"] = biggest_bucket
        context["n_bins"] = n_bins
        context["counts"] = counts
        context["bins"] = bins

        return context


class DataProductViewPerSasID(LoginRequiredMixin, CanAccessWorkSpecificationMixin, TemplateView):
Fanna Lautenbach's avatar
Fanna Lautenbach committed
    template_name = "lofardata/workspecification/dataproducts.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        try:
            specification = self.get_object()
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        except ObjectDoesNotExist:
            return context

        sas_id = specification.filters['obs_id']
        context["sas_id"] = sas_id
        context["dataproduct_info"] = dataproductinfo.retrieve_combined_information(sas_id)
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        return context

    def get_object(self):
        return WorkSpecification.objects.get(pk=self.kwargs['pk'])
Fanna Lautenbach's avatar
Fanna Lautenbach committed

# ---------- REST API views ----------
Mattia Mancini's avatar
Mattia Mancini committed
class DataProductView(generics.ListCreateAPIView):
    model = DataProduct
    serializer_class = DataProductSerializer
Nico Vermaas's avatar
Nico Vermaas committed

    queryset = DataProduct.objects.all().order_by("obs_id")
Nico Vermaas's avatar
Nico Vermaas committed

    # using the Django Filter Backend - https://django-filter.readthedocs.io/en/latest/index.html
    filter_backends = (filters.DjangoFilterBackend,)
Mattia Mancini's avatar
Mattia Mancini committed
    filter_class = DataProductFilterSet


Nico Vermaas's avatar
Nico Vermaas committed
class ATDBProcessingSiteView(viewsets.ReadOnlyModelViewSet):
    model = ATDBProcessingSite
    serializer_class = ATDBProcessingSiteSerializer

Fanna Lautenbach's avatar
Fanna Lautenbach committed
    queryset = ATDBProcessingSite.objects.all().order_by("pk")
class DataProductDetailsView(generics.RetrieveUpdateDestroyAPIView):
    model = DataProduct
    serializer_class = DataProductSerializer
    queryset = DataProduct.objects.all()


class InsertWorkSpecificationSchema(AutoSchema):
    def get_operation_id_base(self, path, method, action):
        return "createDataProductMulti"
Mattia Mancini's avatar
Mattia Mancini committed
class InsertMultiDataproductView(generics.CreateAPIView):
    """
    Add single DataProduct
    """
Mattia Mancini's avatar
Mattia Mancini committed
    queryset = DataProduct.objects.all()
    serializer_class = DataProductFlatSerializer
    schema = InsertWorkSpecificationSchema()
Mattia Mancini's avatar
Mattia Mancini committed

    def get_serializer(self, *args, **kwargs):
        """if an array is passed, set serializer to many"""
        if isinstance(kwargs.get("data", {}), list):
            kwargs["many"] = True
Mattia Mancini's avatar
Mattia Mancini committed
        return super().get_serializer(*args, **kwargs)
class WorkSpecificationViewset(viewsets.ModelViewSet):
Mattia Mancini's avatar
Mattia Mancini committed
    queryset = WorkSpecification.objects.all()
    serializer_class = WorkSpecificationSerializer

    def get_queryset(self):
        current_user: User = self.request.user
        if not current_user.is_staff or not current_user.is_superuser:
            return self.queryset.filter(created_by=current_user.id)
Mattia Mancini's avatar
Mattia Mancini committed
        else:
            return self.queryset

    @action(detail=True, methods=["POST"])
    def submit(self, request, pk=None) -> Response:
        # TODO: check that there are some matches in the request?
Fanna Lautenbach's avatar
Fanna Lautenbach committed
        insert_task_into_atdb.delay(pk)

Fanna Lautenbach's avatar
Fanna Lautenbach committed
        time.sleep(1)  # allow for some time to pass
Fanna Lautenbach's avatar
Fanna Lautenbach committed

Fanna Lautenbach's avatar
Fanna Lautenbach committed
        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

    def form_valid(self, form):
        group = form.save()
        obs_ids = split_obs_ids_string(form.data.get("obs_ids"))
        create_work_specifications_for_group(group, self.request.user, obs_ids)

        return super().form_valid(form)

    def get_success_url(self, **kwargs):
        return reverse_lazy("index")