From 0dd23ea384aa490564dbf961c06ddffc2b54d5a8 Mon Sep 17 00:00:00 2001 From: jkuensem <jkuensem@physik.uni-bielefeld.de> Date: Tue, 17 Mar 2020 10:44:50 +0100 Subject: [PATCH] TMSS-162: Add user references to log table for subtask state changes --- SAS/TMSS/src/tmss/tmssapp/models/scheduling.py | 12 +++++------- .../src/tmss/tmssapp/serializers/scheduling.py | 15 ++++++++++----- .../src/tmss/tmssapp/serializers/specification.py | 9 +++++++++ .../src/tmss/tmssapp/viewsets/specification.py | 10 ++++++++++ SAS/TMSS/src/tmss/urls.py | 2 +- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py index 2260dad7658..95118c912f9 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py @@ -5,6 +5,7 @@ This file contains the database models from django.db.models import ForeignKey, CharField, DateTimeField, BooleanField, IntegerField, BigIntegerField, \ ManyToManyField, CASCADE, SET_NULL, PROTECT from django.contrib.postgres.fields import ArrayField, JSONField +from django.contrib.auth.models import User from .specification import AbstractChoice, BasicCommon, Template, NamedCommon # , <TaskBlueprint from enum import Enum from rest_framework.serializers import HyperlinkedRelatedField @@ -95,6 +96,7 @@ class ScheduleMethod(AbstractChoice): BATCH = 'batch' DYNAMIC = 'dynamic' + # # Templates # @@ -153,10 +155,6 @@ class Subtask(BasicCommon): scheduler_input_doc = JSONField(help_text='Partial specifications, as input for the scheduler.') # resource_claim = ForeignKey("ResourceClaim", null=False, on_delete=PROTECT) # todo <-- how is this external reference supposed to work? - def __init__(self, *args, **kwargs): - super(Subtask, self).__init__(*args, **kwargs) - self.__original_state = self.state - def validate_specification_against_schema(self): if self.specifications_doc is None or self.specifications_template_id is None: return @@ -177,9 +175,6 @@ class Subtask(BasicCommon): '''override of normal save method, doing a validation of the specification against the schema first :raises SpecificationException in case the specification does not validate against the schema''' self.validate_specification_against_schema() - if self.state != self.__original_state: - state_update = SubtaskStateLog(subtask=self, old_state=self.__original_state, new_state=self.state) - state_update.save() super().save(force_insert, force_update, using, update_fields) @@ -187,6 +182,8 @@ class SubtaskStateLog(BasicCommon): """ History of state changes on subtasks """ + user = ForeignKey(User, null=False, editable=False, on_delete=PROTECT, help_text='The user who changed the state of the subtask.') + user_identifier = CharField(null=False, editable=False, max_length=128, help_text='The ID of the user who changed the state of the subtask.') subtask = ForeignKey('Subtask', null=False, editable=False, on_delete=CASCADE, help_text='Subtask to which this state change refers.') old_state = ForeignKey('SubtaskState', null=False, editable=False, on_delete=PROTECT, related_name='is_old_state_of', help_text='Subtask state before update (see Subtask State Machine).') new_state = ForeignKey('SubtaskState', null=False, editable=False, on_delete=PROTECT, related_name='is_new_state_of', help_text='Subtask state after update (see Subtask State Machine).') @@ -265,3 +262,4 @@ class DataproductHash(BasicCommon): dataproduct = ForeignKey('Dataproduct', on_delete=PROTECT, help_text='The dataproduct to which this hash refers.') algorithm = ForeignKey('Algorithm', null=False, on_delete=PROTECT, help_text='Algorithm used (MD5, AES256).') hash = CharField(max_length=128, help_text='Hash value.') + diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py index 2f9b6675123..6a0f6ecebcf 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/scheduling.py @@ -171,11 +171,16 @@ class SubtaskSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializer): # todo: Shall we use one of the default templates for the init? logger.warning('Could not determine schema, hence no fancy JSON form. Expected for list view.') - #def create(self, validated_data): - #subtaskstateupdate_serializer = SubtaskStateLogSerializer(validated_data.get('customer')) - #subtaskstateupdate_serializer.save() - # return User.objects.create(**validated_data) - # = self.context.get('request').user + def update(self, instance, validated_data): + user = self.context.get('request').user + log_entry = models.SubtaskStateLog(user=user, + user_identifier=user.email, + subtask=instance, + old_state=instance.state, + new_state=validated_data.get('state')) + log_entry.save() + super().update(instance, validated_data) + return instance class Meta: model = models.Subtask diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py index e3804396d7e..f71570d16e7 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py @@ -4,6 +4,7 @@ This file contains the serializers (for the elsewhere defined data models) from rest_framework import serializers from .. import models +from django.contrib.auth.models import User from rest_framework import decorators class RelationalHyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer): @@ -17,6 +18,14 @@ class RelationalHyperlinkedModelSerializer(serializers.HyperlinkedModelSerialize return expanded_fields +# This is required for keeping a user reference as ForeignKey in other models +# (I think so that the HyperlinkedModelSerializer can generate a URI) +class UserSerializer(serializers.Serializer): + class Meta: + model = User + fields = '__all__' + + class TagsSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.Tags diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py index b9451879335..2d7b11a7fcf 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py @@ -4,6 +4,8 @@ This file contains the viewsets (based on the elsewhere defined data models and from django.shortcuts import get_object_or_404 from django.http import JsonResponse +from django.contrib.auth.models import User +from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.decorators import permission_classes from rest_framework.permissions import IsAuthenticatedOrReadOnly, DjangoModelPermissions @@ -17,6 +19,14 @@ from lofar.sas.tmss.tmss.tmssapp import serializers from lofar.common.json_utils import get_default_json_object_for_schema + +# This is required for keeping a user reference as ForeignKey in other models +# (I think so that the HyperlinkedModelSerializer can generate a URI) +class UserViewSet(ReadOnlyModelViewSet): + queryset = User.objects.all() + serializer_class = serializers.UserSerializer + + class TagsViewSet(LOFARViewSet): queryset = models.Tags.objects.all() serializer_class = serializers.TagsSerializer diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index 4a97752637e..1f0c91950f4 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -135,7 +135,7 @@ router.register(r'dataproduct_archive_info', viewsets.DataproductArchiveInfoView router.register(r'dataproduct_hash', viewsets.DataproductHashViewSet) router.register(r'task_relation_blueprint', viewsets.TaskRelationBlueprintViewSet) router.register(r'subtask_state_log', viewsets.SubtaskStateLogViewSet) - +router.register(r'user', viewsets.UserViewSet) # --- -- GitLab