Skip to content
Snippets Groups Projects
Commit a328152d authored by Jorrit Schaap's avatar Jorrit Schaap
Browse files

TMSS-206: added easy to use properties successors and predecessors to SubTaks,...

TMSS-206: added easy to use properties successors and predecessors to SubTaks, and expose them via nested urls
parent 714df5cb
No related branches found
No related tags found
1 merge request!158Resolve TMSS-206
...@@ -14,6 +14,7 @@ from .specification import AbstractChoice, BasicCommon, Template, NamedCommon # ...@@ -14,6 +14,7 @@ from .specification import AbstractChoice, BasicCommon, Template, NamedCommon #
from enum import Enum from enum import Enum
from rest_framework.serializers import HyperlinkedRelatedField from rest_framework.serializers import HyperlinkedRelatedField
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.expressions import RawSQL
from lofar.sas.tmss.tmss.tmssapp.validation import validate_json_against_schema from lofar.sas.tmss.tmss.tmssapp.validation import validate_json_against_schema
from lofar.messaging.messagebus import ToBus, DEFAULT_BROKER, DEFAULT_BUSNAME from lofar.messaging.messagebus import ToBus, DEFAULT_BROKER, DEFAULT_BUSNAME
...@@ -172,6 +173,34 @@ class Subtask(BasicCommon): ...@@ -172,6 +173,34 @@ class Subtask(BasicCommon):
content={'subtask_id': subtask_id, 'old_state': old_state, 'new_state': new_state}) content={'subtask_id': subtask_id, 'old_state': old_state, 'new_state': new_state})
tobus.send(msg) tobus.send(msg)
@property
def successors_queryset(self):
'''return the connect successor subtask(s) as queryset (over which you can perform extended queries, or return via the serializers/viewsets)'''
# JS, 20200528: I couldn't make django do a "self-reference" query from the subtask table to the subtask table (via input, output), so I used plain SQL.
return Subtask.objects.filter(id__in=RawSQL("SELECT successor_st.id FROM tmssapp_subtask as successor_st\n"
"INNER JOIN tmssapp_subtaskinput as st_input on st_input.subtask_id = successor_st.id\n"
"INNER JOIN tmssapp_subtaskoutput as st_output on st_output.id = st_input.producer_id\n"
"WHERE st_output.subtask_id = %s", params=[self.id]))
@property
def successors(self):
'''return the connect successor subtask(s) as result of successors_queryset'''
return self.successors_queryset.all()
@property
def predecessors_queryset(self):
'''return the connect predecessor subtask(s) as queryset (over which you can perform extended queries, or return via the serializers/viewsets)'''
# JS, 20200528: I couldn't make django do a "self-reference" query from the subtask table to the subtask table (via input, output), so I used plain SQL.
return Subtask.objects.filter(id__in=RawSQL("SELECT predecessor_st.id FROM tmssapp_subtask as predecessor_st\n"
"INNER JOIN tmssapp_subtaskoutput as st_output on st_output.subtask_id = predecessor_st.id\n"
"INNER JOIN tmssapp_subtaskinput as st_input on st_input.producer_id = st_output.id\n"
"WHERE st_input.subtask_id = %s", params=[self.id]))
@property
def predecessors(self):
'''return the connect predecessor subtask(s) as result of predecessors_queryset'''
return self.predecessors_queryset.all()
def save(self, force_insert=False, force_update=False, using=None, update_fields=None): def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
creating = self._state.adding # True on create, False on update creating = self._state.adding # True on create, False on update
......
...@@ -194,6 +194,14 @@ class SubtaskNestedViewSet(LOFARNestedViewSet): ...@@ -194,6 +194,14 @@ class SubtaskNestedViewSet(LOFARNestedViewSet):
task_blueprint = get_object_or_404(models.TaskBlueprint, pk=self.kwargs['task_blueprint_id']) task_blueprint = get_object_or_404(models.TaskBlueprint, pk=self.kwargs['task_blueprint_id'])
return task_blueprint.subtasks.all() return task_blueprint.subtasks.all()
if 'subtask_id' in self.kwargs:
subtask = get_object_or_404(models.Subtask, pk=self.kwargs['subtask_id'])
if 'successors' in self.request._request.path:
return subtask.successors_queryset
if 'predecessors' in self.request._request.path:
return subtask.predecessors_queryset
class SubtaskInputViewSet(LOFARViewSet): class SubtaskInputViewSet(LOFARViewSet):
queryset = models.SubtaskInput.objects.all() queryset = models.SubtaskInput.objects.all()
......
...@@ -107,6 +107,8 @@ router.register(r'task_draft/(?P<task_draft_id>\d+)/task_relation_draft', viewse ...@@ -107,6 +107,8 @@ router.register(r'task_draft/(?P<task_draft_id>\d+)/task_relation_draft', viewse
router.register(r'task_relation_draft/(?P<task_relation_draft_id>\d+)/task_relation_blueprint', viewsets.TaskRelationBlueprintNestedViewSet) router.register(r'task_relation_draft/(?P<task_relation_draft_id>\d+)/task_relation_blueprint', viewsets.TaskRelationBlueprintNestedViewSet)
router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/task_relation_blueprint', viewsets.TaskRelationBlueprintNestedViewSet) router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/task_relation_blueprint', viewsets.TaskRelationBlueprintNestedViewSet)
router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/subtask', viewsets.SubtaskNestedViewSet) router.register(r'task_blueprint/(?P<task_blueprint_id>\d+)/subtask', viewsets.SubtaskNestedViewSet)
router.register(r'subtask/(?P<subtask_id>\d+)/successors', viewsets.SubtaskNestedViewSet)
router.register(r'subtask/(?P<subtask_id>\d+)/predecessors', viewsets.SubtaskNestedViewSet)
#router.register(r'subtask/(?P<subtask_id>[\w\-]+)/state_log', viewsets.SubtaskStateLogViewSet) #router.register(r'subtask/(?P<subtask_id>[\w\-]+)/state_log', viewsets.SubtaskStateLogViewSet)
# SCHEDULING # SCHEDULING
......
...@@ -211,6 +211,60 @@ class SubtaskTest(unittest.TestCase): ...@@ -211,6 +211,60 @@ class SubtaskTest(unittest.TestCase):
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
models.Subtask.objects.create(**test_data) models.Subtask.objects.create(**test_data)
def test_Subtask_predecessors_and_successors_none(self):
subtask1:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask2:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
self.assertEqual(set(), set(subtask1.predecessors))
self.assertEqual(set(), set(subtask2.predecessors))
self.assertEqual(set(), set(subtask1.successors))
self.assertEqual(set(), set(subtask2.successors))
def test_Subtask_predecessors_and_successors_simple(self):
subtask1:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask2:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
output1 = models.SubtaskOutput.objects.create(subtask=subtask1)
models.SubtaskInput.objects.create(**SubtaskInput_test_data(subtask=subtask2, producer=output1))
self.assertEqual(subtask1, subtask2.predecessors[0])
self.assertEqual(subtask2, subtask1.successors[0])
def test_Subtask_predecessors_and_successors_complex(self):
subtask1:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask2:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask3:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask4:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask5:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
subtask6:models.Subtask = models.Subtask.objects.create(**Subtask_test_data())
# ST1 ---> ST3 ---> ST4
# | |
# ST2 - -> ST5 ---> ST6
output1 = models.SubtaskOutput.objects.create(subtask=subtask1)
output2 = models.SubtaskOutput.objects.create(subtask=subtask2)
output3 = models.SubtaskOutput.objects.create(subtask=subtask3)
output4 = models.SubtaskOutput.objects.create(subtask=subtask4)
output5 = models.SubtaskOutput.objects.create(subtask=subtask5)
output6 = models.SubtaskOutput.objects.create(subtask=subtask6)
models.SubtaskInput.objects.create(**SubtaskInput_test_data(subtask=subtask3, producer=output1))
models.SubtaskInput.objects.create(**SubtaskInput_test_data(subtask=subtask3, producer=output2))
models.SubtaskInput.objects.create(**SubtaskInput_test_data(subtask=subtask4, producer=output3))
models.SubtaskInput.objects.create(**SubtaskInput_test_data(subtask=subtask5, producer=output3))
models.SubtaskInput.objects.create(**SubtaskInput_test_data(subtask=subtask6, producer=output5))
self.assertEqual(set((subtask1, subtask2)), set(subtask3.predecessors))
self.assertEqual(set((subtask4, subtask5)), set(subtask3.successors))
self.assertEqual(set((subtask3,)), set(subtask4.predecessors))
self.assertEqual(set((subtask3,)), set(subtask5.predecessors))
self.assertEqual(set((subtask3,)), set(subtask1.successors))
self.assertEqual(set((subtask3,)), set(subtask2.successors))
self.assertEqual(set(), set(subtask1.predecessors))
self.assertEqual(set(), set(subtask2.predecessors))
self.assertEqual(set(), set(subtask4.successors))
self.assertEqual(set((subtask6,)), set(subtask5.successors))
class DataproductTest(unittest.TestCase): class DataproductTest(unittest.TestCase):
def test_Dataproduct_gets_created_with_correct_creation_timestamp(self): def test_Dataproduct_gets_created_with_correct_creation_timestamp(self):
......
...@@ -252,11 +252,13 @@ def SubtaskOutput_test_data(subtask: models.Subtask=None) -> dict: ...@@ -252,11 +252,13 @@ def SubtaskOutput_test_data(subtask: models.Subtask=None) -> dict:
return {"subtask": subtask, return {"subtask": subtask,
"tags":[]} "tags":[]}
def SubtaskInput_test_data() -> dict: def SubtaskInput_test_data(subtask: models.Subtask=None, producer: models.SubtaskOutput=None) -> dict:
return {"subtask": models.Subtask.objects.create(**Subtask_test_data()), if subtask is None:
subtask = models.Subtask.objects.create(**Subtask_test_data())
return {"subtask": subtask,
"task_relation_blueprint": models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data()), "task_relation_blueprint": models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data()),
"producer": models.SubtaskOutput.objects.create(**SubtaskOutput_test_data()), "producer": producer,
#"dataproducts": models.Dataproduct.objects.create(**dpt.get_test_data()),
"selection_doc": {}, "selection_doc": {},
"selection_template": models.SubtaskInputSelectionTemplate.objects.create(**SubtaskInputSelectionTemplate_test_data()), "selection_template": models.SubtaskInputSelectionTemplate.objects.create(**SubtaskInputSelectionTemplate_test_data()),
"tags":[]} "tags":[]}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment