...
 
Commits (19)
# Generated by Django 3.0.7 on 2020-06-22 14:33
# Generated by Django 2.2.12 on 2020-07-07 10:45
from django.conf import settings
import django.contrib.postgres.fields
......@@ -307,15 +307,6 @@ class Migration(migrations.Migration):
('create_function', models.CharField(help_text='Python function to call to execute the generator.', max_length=128)),
],
),
migrations.CreateModel(
name='Placement',
fields=[
('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Project',
fields=[
......@@ -385,6 +376,15 @@ class Migration(migrations.Migration):
'abstract': False,
},
),
migrations.CreateModel(
name='SchedulingRelationPlacement',
fields=[
('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='SchedulingSet',
fields=[
......@@ -639,7 +639,7 @@ class Migration(migrations.Migration):
('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
('time_offset', models.FloatField(help_text='Time Offset between first and second Task Blueprint')),
('time_offset', models.IntegerField(default=60, help_text='Time offset of start of second task with respect to start of first task.')),
],
options={
'abstract': False,
......@@ -652,7 +652,7 @@ class Migration(migrations.Migration):
('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
('time_offset', models.FloatField(help_text='Time Offset between first and second Task Draft')),
('time_offset', models.IntegerField(default=60, help_text='Time offset of start of second task with respect to start of first task.')),
],
options={
'abstract': False,
......@@ -691,33 +691,33 @@ class Migration(migrations.Migration):
),
migrations.AddField(
model_name='taskschedulingrelationdraft',
name='placement',
field=models.ForeignKey(help_text='Placement of first and second Task Draft', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Placement'),
name='first',
field=models.ForeignKey(help_text='First Task Draft to connect.', on_delete=django.db.models.deletion.CASCADE, related_name='first_to_connect', to='tmssapp.TaskDraft'),
),
migrations.AddField(
model_name='taskschedulingrelationdraft',
name='first',
field=models.ForeignKey(help_text='Name of first Task Draft', on_delete=django.db.models.deletion.CASCADE, related_name='scheduling_relation_first', to='tmssapp.TaskDraft'),
name='placement',
field=models.ForeignKey(help_text='Task scheduling relation placement.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SchedulingRelationPlacement'),
),
migrations.AddField(
model_name='taskschedulingrelationdraft',
name='second',
field=models.ForeignKey(help_text='Name of second Task Draft.', on_delete=django.db.models.deletion.CASCADE, related_name='scheduling_relation_second', to='tmssapp.TaskDraft'),
field=models.ForeignKey(help_text='Second Task Draft to connect.', on_delete=django.db.models.deletion.CASCADE, related_name='second_to_connect', to='tmssapp.TaskDraft'),
),
migrations.AddField(
model_name='taskschedulingrelationblueprint',
name='placement',
field=models.ForeignKey(help_text='Placement of first and second Task Blueprint', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Placement'),
name='first',
field=models.ForeignKey(help_text='First Task Blueprint to connect.', on_delete=django.db.models.deletion.CASCADE, related_name='first_to_connect', to='tmssapp.TaskBlueprint'),
),
migrations.AddField(
model_name='taskschedulingrelationblueprint',
name='first',
field=models.ForeignKey(help_text='Name of first Task Blueprint', on_delete=django.db.models.deletion.CASCADE, related_name='scheduling_relation_first', to='tmssapp.TaskBlueprint'),
name='placement',
field=models.ForeignKey(default='after', help_text='Task scheduling relation placement.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SchedulingRelationPlacement'),
),
migrations.AddField(
model_name='taskschedulingrelationblueprint',
name='second',
field=models.ForeignKey(help_text='Name of second Task Blueprint.', on_delete=django.db.models.deletion.CASCADE, related_name='scheduling_relation_second', to='tmssapp.TaskBlueprint'),
field=models.ForeignKey(help_text='Second Task Blueprint to connect.', on_delete=django.db.models.deletion.CASCADE, related_name='second_to_connect', to='tmssapp.TaskBlueprint'),
),
migrations.AddConstraint(
model_name='taskrelationselectiontemplate',
......
......@@ -9,6 +9,8 @@ from enum import Enum
from django.db.models.expressions import RawSQL
from lofar.sas.tmss.tmss.tmssapp.validation import validate_json_against_schema
from django.core.exceptions import ValidationError
from rest_framework import status
#
# Common
......@@ -131,6 +133,14 @@ class CopyReason(AbstractChoice):
REPEATED = "repeated"
class SchedulingRelationPlacement(AbstractChoice):
"""Defines the model and predefined list of possible Placements for Task Scheduling Relation.
The items in the Choices class below are automagically populated into the database via a data migration."""
class Choices(Enum):
AFTER = "after"
BEFORE = "before"
PARALLEL = "parallel"
class Flag(AbstractChoice):
"""Defines the model and predefined list of possible Flags to be used in Setting.
The items in the Choises class below are automagically populated into the database via a data migration."""
......@@ -138,15 +148,6 @@ class Flag(AbstractChoice):
AUTOSCHEDULE = "allow_scheduling_observations"
class Placement(AbstractChoice):
"""Defines the model and predefined list of possible Placements for Task Scheduling Relation.
The items in the Choices class below are automagically populated into the database via a data migration."""
class Choices(Enum):
BEFORE = "before"
AFTER = "after"
PARALLEL = "parallel"
# concrete models
class Setting(BasicCommon):
......@@ -412,15 +413,30 @@ class TaskRelationBlueprint(BasicCommon):
super().save(force_insert, force_update, using, update_fields)
class TaskSchedulingRelationBlueprint(BasicCommon):
first = ForeignKey('TaskBlueprint', related_name='first_to_connect', on_delete=CASCADE, help_text='First Task Blueprint to connect.')
second = ForeignKey('TaskBlueprint', related_name='second_to_connect', on_delete=CASCADE, help_text='Second Task Blueprint to connect.')
placement = ForeignKey('SchedulingRelationPlacement', null=False, default="after", on_delete=PROTECT, help_text='Task scheduling relation placement.')
time_offset = IntegerField(default=60, help_text='Time offset of start of second task with respect to start of first task.')
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if self.first == self.second:
raise ValidationError("First and Second Task Draft must be different.")
if (str(self.placement) == SchedulingRelationPlacement.Choices.BEFORE.value or str(self.placement) == SchedulingRelationPlacement.Choices.AFTER.value) and self.time_offset<0:
raise ValidationError("Time_offset must be >= 0")
super().save(force_insert, force_update, using, update_fields)
class TaskSchedulingRelationDraft(BasicCommon):
placement = ForeignKey('Placement', null=False, on_delete=PROTECT, help_text='Placement of first and second Task Draft')
time_offset = FloatField(help_text='Time Offset between first and second Task Draft')
first = ForeignKey('TaskDraft', related_name='scheduling_relation_first', on_delete=CASCADE, help_text='Name of first Task Draft')
second = ForeignKey('TaskDraft', related_name='scheduling_relation_second', on_delete=CASCADE, help_text='Name of second Task Draft.')
first = ForeignKey('TaskDraft', related_name='first_to_connect', on_delete=CASCADE, help_text='First Task Draft to connect.')
second = ForeignKey('TaskDraft', related_name='second_to_connect', on_delete=CASCADE, help_text='Second Task Draft to connect.')
placement = ForeignKey('SchedulingRelationPlacement', null=False, on_delete=PROTECT, help_text='Task scheduling relation placement.')
time_offset = IntegerField(default=60, help_text='Time offset of start of second task with respect to start of first task.')
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if self.first == self.second:
raise ValidationError("First and Second Task Draft must be different.")
if (str(self.placement) == SchedulingRelationPlacement.Choices.BEFORE.value or str(self.placement) == SchedulingRelationPlacement.Choices.AFTER.value) and self.time_offset<0:
raise ValidationError("Time_offset must be >= 0")
super().save(force_insert, force_update, using, update_fields)
class TaskSchedulingRelationBlueprint(BasicCommon):
placement = ForeignKey('Placement', null=False, on_delete=PROTECT, help_text='Placement of first and second Task Blueprint')
time_offset = FloatField(help_text='Time Offset between first and second Task Blueprint')
first = ForeignKey('TaskBlueprint', related_name='scheduling_relation_first', on_delete=CASCADE, help_text='Name of first Task Blueprint')
second = ForeignKey('TaskBlueprint', related_name='scheduling_relation_second', on_delete=CASCADE, help_text='Name of second Task Blueprint.')
......@@ -34,8 +34,8 @@ def populate_choices(apps, schema_editor):
each 'choice'type in Role, Datatype, Dataformat, CopyReason
:return: None
'''
for choice_class in [Role, Datatype, Dataformat, CopyReason, Placement, Flag,
SubtaskState, SubtaskType, StationType, Algorithm, ScheduleMethod]:
for choice_class in [Role, Datatype, Dataformat, CopyReason,
SubtaskState, SubtaskType, StationType, Algorithm, ScheduleMethod, SchedulingRelationPlacement, Flag]:
choice_class.objects.bulk_create([choice_class(value=x.value) for x in choice_class.Choices])
def populate_settings(apps, schema_editor):
......
......@@ -135,6 +135,11 @@ class RoleSerializer(serializers.ModelSerializer):
model = models.Role
fields = '__all__'
class SchedulingRelationPlacementSerializer(serializers.ModelSerializer):
class Meta:
model = models.SchedulingRelationPlacement
fields = '__all__'
class DatatypeSerializer(serializers.ModelSerializer):
class Meta:
......@@ -274,7 +279,7 @@ class TaskDraftSerializer(RelationalHyperlinkedModelSerializer):
class Meta:
model = models.TaskDraft
fields = '__all__'
extra_fields = ['task_blueprints', 'produced_by', 'consumed_by', 'scheduling_relation_first', 'scheduling_relation_second']
extra_fields = ['task_blueprints', 'produced_by', 'consumed_by', 'first_to_connect', 'second_to_connect']
class TaskBlueprintSerializer(RelationalHyperlinkedModelSerializer):
......@@ -291,7 +296,7 @@ class TaskBlueprintSerializer(RelationalHyperlinkedModelSerializer):
class Meta:
model = models.TaskBlueprint
fields = '__all__'
extra_fields = ['subtasks', 'produced_by', 'consumed_by', 'scheduling_relation_first', 'scheduling_relation_second']
extra_fields = ['subtasks', 'produced_by', 'consumed_by', 'first_to_connect', 'second_to_connect']
class TaskRelationDraftSerializer(RelationalHyperlinkedModelSerializer):
......@@ -327,19 +332,17 @@ class TaskRelationBlueprintSerializer(RelationalHyperlinkedModelSerializer):
fields = '__all__'
class PlacementSerializer(serializers.ModelSerializer):
class Meta:
model = models.Placement
fields = '__all__'
class TaskSchedulingRelationDraftSerializer(serializers.HyperlinkedModelSerializer):
class TaskSchedulingRelationDraftSerializer(RelationalHyperlinkedModelSerializer):
class Meta:
model = models.TaskSchedulingRelationDraft
fields = '__all__'
class TaskSchedulingRelationBlueprintSerializer(serializers.HyperlinkedModelSerializer):
class TaskSchedulingRelationBlueprintSerializer(RelationalHyperlinkedModelSerializer):
class Meta:
model = models.TaskSchedulingRelationBlueprint
fields = '__all__'
from lofar.sas.tmss.tmss.tmssapp import models
from lofar.sas.tmss.tmss.tmssapp.models.specification import TaskBlueprint, SchedulingUnitBlueprint, TaskDraft, Placement
from lofar.sas.tmss.tmss.tmssapp.models.specification import TaskBlueprint, SchedulingUnitBlueprint, TaskDraft, SchedulingRelationPlacement
from lofar.sas.tmss.tmss.tmssapp.subtasks import create_and_schedule_subtasks_from_task_blueprint
from lofar.sas.tmss.tmss.tmssapp.models.specification import TaskBlueprint, SchedulingUnitBlueprint
from lofar.sas.tmss.tmss.tmssapp.subtasks import create_and_schedule_subtasks_from_task_blueprint, \
......@@ -89,7 +89,7 @@ def create_task_drafts_from_scheduling_unit_draft(scheduling_unit_draft: models.
list_task_scheduling_relations = []
for task_scheduling_relation in list_task_scheduling_relations:
task_rel_sch_obj = models.TaskSchedulingRelationDraft.objects.create(
placement=models.Placement.objects.get(value=task_scheduling_relation["placement"]),
placement=models.SchedulingRelationPlacement.objects.get(value=task_scheduling_relation["placement"]),
time_offset=task_scheduling_relation["time_offset"],
first=models.TaskDraft.objects.get(name=task_scheduling_relation["first"]),
second=models.TaskDraft.objects.get(name=task_scheduling_relation["second"]))
......
......@@ -68,22 +68,18 @@ class SubtaskTypeViewSet(LOFARViewSet):
queryset = models.SubtaskType.objects.all()
serializer_class = serializers.SubtaskTypeSerializer
class StationTypeViewSet(LOFARViewSet):
queryset = models.StationType.objects.all()
serializer_class = serializers.StationTypeSerializer
class AlgorithmViewSet(LOFARViewSet):
queryset = models.Algorithm.objects.all()
serializer_class = serializers.AlgorithmSerializer
class ScheduleMethodViewSet(LOFARViewSet):
queryset = models.ScheduleMethod.objects.all()
serializer_class = serializers.ScheduleMethodSerializer
class SubtaskTemplateFilter(filters.FilterSet):
class Meta:
model = models.SubtaskTemplate
......
......@@ -102,6 +102,11 @@ class RoleViewSet(LOFARViewSet):
serializer_class = serializers.RoleSerializer
class SchedulingRelationPlacement(LOFARViewSet):
queryset = models.SchedulingRelationPlacement.objects.all()
serializer_class = serializers.SchedulingRelationPlacementSerializer
class DatatypeViewSet(LOFARViewSet):
queryset = models.Datatype.objects.all()
serializer_class = serializers.DatatypeSerializer
......@@ -116,9 +121,11 @@ class CopyReasonViewSet(LOFARViewSet):
queryset = models.CopyReason.objects.all()
serializer_class = serializers.CopyReasonSerializer
class ResourceUnitViewSet(LOFARViewSet):
queryset = models.ResourceUnit.objects.all()
serializer_class = serializers.ResourceUnitSerializer
class TaskConnectorTypeViewSet(LOFARViewSet):
queryset = models.TaskConnectorType.objects.all()
......@@ -149,6 +156,7 @@ class ProjectNestedViewSet(LOFARNestedViewSet):
else:
return models.Project.objects.all()
class ProjectQuotaViewSet(LOFARViewSet):
queryset = models.ProjectQuota.objects.all()
serializer_class = serializers.ProjectQuotaSerializer
......@@ -213,7 +221,6 @@ class SchedulingUnitDraftViewSet(LOFARViewSet):
return Response(serializers.SchedulingUnitBlueprintSerializer(scheduling_unit_blueprint, context={'request':request}).data,
status=status.HTTP_201_CREATED,
headers={'Location': scheduling_unit_blueprint_path})
@swagger_auto_schema(responses={201: 'The Created SchedulingUnitBlueprint, see Location in Response header',
403: 'forbidden'},
operation_description="Carve this SchedulingUnitDraft and its TaskDraft(s) in stone, and make blueprint(s) out of it and create their subtask(s)")
......@@ -503,6 +510,15 @@ class TaskRelationBlueprintViewSet(LOFARViewSet):
serializer_class = serializers.TaskRelationBlueprintSerializer
class TaskSchedulingRelationBlueprintViewSet(LOFARViewSet):
queryset = models.TaskSchedulingRelationBlueprint.objects.all()
serializer_class = serializers.TaskSchedulingRelationBlueprintSerializer
class TaskSchedulingRelationDraftViewSet(LOFARViewSet):
queryset = models.TaskSchedulingRelationDraft.objects.all()
serializer_class = serializers.TaskSchedulingRelationDraftSerializer
class TaskRelationBlueprintNestedViewSet(LOFARNestedViewSet):
queryset = models.TaskRelationBlueprint.objects.all()
......@@ -518,16 +534,3 @@ class TaskRelationBlueprintNestedViewSet(LOFARNestedViewSet):
return task_relation_draft.related_task_relation_blueprint.all()
else:
return models.TaskRelationBlueprint.objects.all()
class PlacementViewSet(LOFARViewSet):
queryset = models.Placement.objects.all()
serializer_class = serializers.PlacementSerializer
class TaskSchedulingRelationBlueprintViewSet(LOFARViewSet):
queryset = models.TaskSchedulingRelationBlueprint.objects.all()
serializer_class = serializers.TaskSchedulingRelationBlueprintSerializer
class TaskSchedulingRelationDraftViewSet(LOFARViewSet):
queryset = models.TaskSchedulingRelationDraft.objects.all()
serializer_class = serializers.TaskSchedulingRelationDraftSerializer
\ No newline at end of file
......@@ -86,7 +86,6 @@ router.register(r'role', viewsets.RoleViewSet)
router.register(r'datatype', viewsets.DatatypeViewSet)
router.register(r'dataformat', viewsets.DataformatViewSet)
router.register(r'copy_reason', viewsets.CopyReasonViewSet)
router.register(r'placement', viewsets.PlacementViewSet)
router.register(r'flag', viewsets.FlagViewSet)
# templates
......@@ -137,6 +136,7 @@ router.register(r'subtask_type', viewsets.SubtaskTypeViewSet)
router.register(r'station_type', viewsets.StationTypeViewSet)
router.register(r'algorithm', viewsets.AlgorithmViewSet)
router.register(r'schedule_method', viewsets.ScheduleMethodViewSet)
router.register(r'scheduling_relation_placement', viewsets.SchedulingRelationPlacement)
# templates
router.register(r'subtask_template', viewsets.SubtaskTemplateViewSet)
......
......@@ -246,6 +246,31 @@ def SubtaskTemplate_test_data(schema: object=None, version:str=None) -> dict:
"queue": False,
"tags": ["TMSS", "TESTING"]}
def TaskSchedulingRelationDraft_test_data(first: models.TaskDraft = None, second: models.TaskDraft = None) -> dict:
if first is None:
first = models.TaskDraft.objects.create(**TaskDraft_test_data())
if second is None:
second = models.TaskDraft.objects.create(**TaskDraft_test_data())
return {"tags": [],
"first": first,
"second": second,
"placement": models.SchedulingRelationPlacement.objects.get(value='after'),
"time_offset":60}
def TaskSchedulingRelationBlueprint_test_data(first: models.TaskBlueprint = None, second: models.TaskBlueprint = None) -> dict:
if first is None:
first = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
if second is None:
second = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
return {"tags": [],
"first": first,
"second": second,
"placement": models.SchedulingRelationPlacement.objects.get(value='after'),
"time_offset":60}
def DataproductSpecificationsTemplate_test_data(version:str=None) -> dict:
if version is None:
version = str(uuid.uuid4())
......
......@@ -226,8 +226,8 @@ class TMSSRESTTestDataCreator():
'task_blueprints': [],
'produced_by': [],
'consumed_by': [],
'scheduling_relation_first': [],
'scheduling_relation_second': []}
'first_to_connect': [],
'second_to_connect': []}
def TaskRelationDraft(self, producer_url=None, consumer_url=None, template_url=None, input_role_url=None, output_role_url=None):
......@@ -294,8 +294,8 @@ class TMSSRESTTestDataCreator():
"subtasks": [],
"produced_by": [],
"consumed_by": [],
'scheduling_relation_first': [],
'scheduling_relation_second': []}
'first_to_connect': [],
'second_to_connect': []}
def TaskRelationBlueprint(self, draft_url=None, template_url=None, input_role_url=None, output_role_url=None, consumer_url=None, producer_url=None):
if draft_url is None:
......@@ -346,6 +346,32 @@ class TMSSRESTTestDataCreator():
"queue": False,
"tags": ["TMSS", "TESTING"]}
def TaskSchedulingRelationBlueprint(self, first_url=None, second_url=None, placement="after"):
if first_url is None:
first_url = self.post_data_and_get_url(self.TaskBlueprint(), '/task_blueprint/')
if second_url is None:
second_url = self.post_data_and_get_url(self.TaskBlueprint(), '/task_blueprint/')
return {"tags": [],
"first": first_url,
"second": second_url,
"placement": self.django_api_url + '/scheduling_relation_placement/%s/'%placement,
"time_offset":60}
def TaskSchedulingRelationDraft(self, first_url=None, second_url=None, placement="after"):
if first_url is None:
first_url = self.post_data_and_get_url(self.TaskDraft(), '/task_draft/')
if second_url is None:
second_url = self.post_data_and_get_url(self.TaskDraft(), '/task_draft/')
return {"tags": [],
"first": first_url,
"second": second_url,
"placement": self.django_api_url + '/scheduling_relation_placement/%s/'%placement,
"time_offset":60}
def DataproductSpecificationsTemplate(self, name="my_DataproductSpecificationsTemplate", version:str=None) -> dict:
if version is None:
version = str(uuid.uuid4())
......