diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py index c6b66991f0846e93a18d26d7c131a40a94b5ff87..fc070c79167afd4e55e76c20d8ae39db1ba8f961 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.9 on 2021-03-23 17:08 +# Generated by Django 3.0.9 on 2021-03-29 13:02 from django.conf import settings import django.contrib.postgres.fields @@ -6,6 +6,7 @@ import django.contrib.postgres.fields.jsonb import django.contrib.postgres.indexes from django.db import migrations, models import django.db.models.deletion +import lofar.sas.tmss.tmss.tmssapp.models.common import lofar.sas.tmss.tmss.tmssapp.models.specification @@ -98,6 +99,7 @@ class Migration(migrations.Migration): options={ 'abstract': False, }, + bases=(lofar.sas.tmss.tmss.tmssapp.models.common.RefreshFromDbInvalidatesCachedPropertiesMixin, models.Model), ), migrations.CreateModel( name='CycleQuota', @@ -378,6 +380,15 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.CreateModel( + name='PriorityQueueType', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), migrations.CreateModel( name='Project', fields=[ @@ -398,6 +409,7 @@ class Migration(migrations.Migration): options={ 'abstract': False, }, + bases=(lofar.sas.tmss.tmss.tmssapp.models.common.RefreshFromDbInvalidatesCachedPropertiesMixin, models.Model), ), migrations.CreateModel( name='ProjectCategory', @@ -433,6 +445,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ], + bases=(lofar.sas.tmss.tmss.tmssapp.models.common.RefreshFromDbInvalidatesCachedPropertiesMixin, models.Model), ), migrations.CreateModel( name='ProjectRole', @@ -608,10 +621,12 @@ class Migration(migrations.Migration): ('output_data_allowed_to_be_ingested', models.BooleanField(default=False, help_text='boolean (default FALSE), which blocks Ingest Tasks from starting if OFF. When toggled ON, backend must scan for startable Ingest Tasks.')), ('output_pinned', models.BooleanField(default=False, help_text='boolean (default FALSE), which blocks deleting unpinned dataproducts. When toggled ON, backend must pick SUB up for deletion. It also must when dataproducts are unpinned.')), ('results_accepted', models.BooleanField(default=False, help_text='boolean (default NULL), which records whether the results were accepted, allowing the higher-level accounting to be adjusted.')), + ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')), ], options={ 'abstract': False, }, + bases=(lofar.sas.tmss.tmss.tmssapp.models.common.RefreshFromDbInvalidatesCachedPropertiesMixin, models.Model), ), migrations.CreateModel( name='SchedulingUnitDraft', @@ -626,10 +641,12 @@ class Migration(migrations.Migration): ('generator_instance_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Parameter value that generated this run draft (NULLable).', null=True)), ('scheduling_constraints_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling Constraints for this run.', null=True)), ('ingest_permission_required', models.BooleanField(default=False, help_text='Explicit permission is needed before the task.')), + ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')), ], options={ 'abstract': False, }, + bases=(lofar.sas.tmss.tmss.tmssapp.models.common.RefreshFromDbInvalidatesCachedPropertiesMixin, models.Model), ), migrations.CreateModel( name='SchedulingUnitObservingStrategyTemplate', @@ -791,6 +808,7 @@ class Migration(migrations.Migration): ('do_cancel', models.BooleanField(help_text='Cancel this task.')), ('output_pinned', models.BooleanField(default=False, help_text='True if the output of this task is pinned to disk, that is, forbidden to be removed.')), ], + bases=(lofar.sas.tmss.tmss.tmssapp.models.common.RefreshFromDbInvalidatesCachedPropertiesMixin, models.Model), ), migrations.CreateModel( name='TaskConnectorType', @@ -1173,6 +1191,11 @@ class Migration(migrations.Migration): name='observation_strategy_template', field=models.ForeignKey(help_text='Observation Strategy Template used to create the requirements_doc.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SchedulingUnitObservingStrategyTemplate'), ), + migrations.AddField( + model_name='schedulingunitdraft', + name='priority_queue', + field=models.ForeignKey(default='A', help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.PriorityQueueType'), + ), migrations.AddField( model_name='schedulingunitdraft', name='requirements_template', @@ -1193,6 +1216,11 @@ class Migration(migrations.Migration): name='draft', field=models.ForeignKey(help_text='Scheduling Unit Draft which this run instantiates.', on_delete=django.db.models.deletion.PROTECT, related_name='scheduling_unit_blueprints', to='tmssapp.SchedulingUnitDraft'), ), + migrations.AddField( + model_name='schedulingunitblueprint', + name='priority_queue', + field=models.ForeignKey(default='A', help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.PriorityQueueType'), + ), migrations.AddField( model_name='schedulingunitblueprint', name='requirements_template', diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index 140b298db576485d9f3d8f23cb49f20daf15cd37..21c56bb84ca9393c50c745a632dc70d34a5d4815 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -156,6 +156,13 @@ class TaskType(AbstractChoice): OTHER = 'other' +class PriorityQueueType(AbstractChoice): + """Defines the possible priority queues for SchedulingUnits. + The items in the Choices class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + A = "A" + B = "B" + # concrete models class Setting(BasicCommon): @@ -387,6 +394,8 @@ class SchedulingUnitDraft(RefreshFromDbInvalidatesCachedPropertiesMixin, NamedCo scheduling_constraints_doc = JSONField(help_text='Scheduling Constraints for this run.', null=True) scheduling_constraints_template = ForeignKey('SchedulingConstraintsTemplate', on_delete=CASCADE, null=True, help_text='Schema used for scheduling_constraints_doc.') ingest_permission_required = BooleanField(default=False, help_text='Explicit permission is needed before the task.') + priority_rank = FloatField(null=False, default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.') + priority_queue = ForeignKey('PriorityQueueType', null=False, on_delete=PROTECT, default="A", help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.requirements_doc is not None and self.requirements_template_id and self.requirements_template.schema is not None: @@ -463,6 +472,8 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Nam output_data_allowed_to_be_ingested = BooleanField(default=False, help_text='boolean (default FALSE), which blocks Ingest Tasks from starting if OFF. When toggled ON, backend must scan for startable Ingest Tasks.') output_pinned = BooleanField(default=False, help_text='boolean (default FALSE), which blocks deleting unpinned dataproducts. When toggled ON, backend must pick SUB up for deletion. It also must when dataproducts are unpinned.') results_accepted = BooleanField(default=False, help_text='boolean (default NULL), which records whether the results were accepted, allowing the higher-level accounting to be adjusted.') + priority_rank = FloatField(null=False, default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.') + priority_queue = ForeignKey('PriorityQueueType', null=False, on_delete=PROTECT, default="A", help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): annotate_validate_add_defaults_to_doc_using_template(self, 'requirements_doc', 'requirements_template') diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py index 13b671a4c57c8d225e9ba930d7846bde4227fe88..684280c9ad39c7828f4e0be3bf121ff3b97fde3e 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py @@ -43,7 +43,7 @@ def populate_choices(apps, schema_editor): ''' choice_classes = [Role, IOType, Datatype, Dataformat, CopyReason, SubtaskState, SubtaskType, StationType, Algorithm, SchedulingRelationPlacement, - Flag, ProjectCategory, PeriodCategory, Quantity, TaskType, ProjectRole] + Flag, ProjectCategory, PeriodCategory, Quantity, TaskType, ProjectRole, PriorityQueueType] # upload choices in parallel with ThreadPoolExecutor() as executor: diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py index 8e21947208819f013ba1c7d23bda3586cd774f91..fc23e9e94249066fdd813ea2ece5ad199bd2f452 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py @@ -369,6 +369,12 @@ class TaskTypeSerializer(DynamicRelationalHyperlinkedModelSerializer): fields = '__all__' +class PriorityQueueTypeSerializer(DynamicRelationalHyperlinkedModelSerializer): + class Meta: + model = models.PriorityQueueType + fields = '__all__' + + class ReservationStrategyTemplateSerializer(DynamicRelationalHyperlinkedModelSerializer): template = JSONEditorField(schema_source="reservation_template.schema") diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py index 620742eaa77f9aedd8400e88f862121fcb2e2dbf..49ddf7a09713dd394c1265d8baf1dbcbcc29121a 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py @@ -1081,3 +1081,8 @@ class TaskTypeViewSet(LOFARViewSet): queryset = models.TaskType.objects.all() serializer_class = serializers.TaskTypeSerializer + +class PriorityQueueTypeViewSet(LOFARViewSet): + queryset = models.PriorityQueueType.objects.all() + serializer_class = serializers.PriorityQueueTypeSerializer + diff --git a/SAS/TMSS/backend/src/tmss/urls.py b/SAS/TMSS/backend/src/tmss/urls.py index a73ba904d3a9d3653a9ab4c638c58306b30537af..e45c9db4013e4025570156045e609b22d30df240 100644 --- a/SAS/TMSS/backend/src/tmss/urls.py +++ b/SAS/TMSS/backend/src/tmss/urls.py @@ -126,6 +126,7 @@ router.register(r'period_category', viewsets.PeriodCategoryViewSet) router.register(r'project_category', viewsets.ProjectCategoryViewSet) router.register(r'quantity', viewsets.QuantityViewSet) router.register(r'task_type', viewsets.TaskTypeViewSet) +router.register(r'priority_queue_type', viewsets.PriorityQueueTypeViewSet) # templates router.register(r'common_schema_template', viewsets.CommonSchemaTemplateViewSet)