diff --git a/SAS/TMSS/src/remakemigrations.py b/SAS/TMSS/src/remakemigrations.py index 503432045e7905079d295a0faba956a987eaa66c..397b52ac2375b695d88312597fcb540532e7fb2d 100755 --- a/SAS/TMSS/src/remakemigrations.py +++ b/SAS/TMSS/src/remakemigrations.py @@ -79,8 +79,8 @@ class Migration(migrations.Migration): migrations.RunPython(populate_settings), migrations.RunPython(populate_misc), migrations.RunPython(populate_lofar_json_schemas), - migrations.RunPython(populate_cycles), migrations.RunPython(populate_resources), + migrations.RunPython(populate_cycles), migrations.RunPython(populate_projects), migrations.RunPython(populate_test_scheduling_set) ] """ diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py index 25c030c9a7a6c51a724f314f45d526699f13e5fe..b04ecd0b93b65653a7426a081eb419c03ddef895 100644 --- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.12 on 2020-08-03 10:00 +# Generated by Django 2.2.12 on 2020-08-04 12:35 from django.conf import settings import django.contrib.postgres.fields @@ -354,6 +354,15 @@ class Migration(migrations.Migration): ('value', models.FloatField(help_text='Resource Quota value')), ], ), + migrations.CreateModel( + name='Quantity', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), migrations.CreateModel( name='ResourceType', fields=[ @@ -991,10 +1000,15 @@ class Migration(migrations.Migration): name='project', field=models.ForeignKey(help_text='Project to which this scheduling set belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='scheduling_sets', to='tmssapp.Project'), ), + migrations.AddField( + model_name='resourcetype', + name='quantity', + field=models.ForeignKey(help_text='The quantity of this resource type.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Quantity'), + ), migrations.AddField( model_name='projectquota', name='project', - field=models.ForeignKey(help_text='Project to wich this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='project_quota', to='tmssapp.Project'), + field=models.ForeignKey(help_text='Project to wich this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='quota', to='tmssapp.Project'), ), migrations.AddField( model_name='projectquota', @@ -1111,7 +1125,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cyclequota', name='cycle', - field=models.ForeignKey(help_text='Cycle to which these quota apply.', on_delete=django.db.models.deletion.PROTECT, related_name='cycle_quota', to='tmssapp.Cycle'), + field=models.ForeignKey(help_text='Cycle to which these quota apply.', on_delete=django.db.models.deletion.PROTECT, related_name='quota', to='tmssapp.Cycle'), ), migrations.AddField( model_name='cyclequota', diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py index 58b591cf062f6347698af904e25dd99bbd9e21ed..b24c0bccf0ded89c957fa2c6c53c62dea7428c71 100644 --- a/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py +++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): migrations.RunPython(populate_settings), migrations.RunPython(populate_misc), migrations.RunPython(populate_lofar_json_schemas), - migrations.RunPython(populate_resources), migrations.RunPython(populate_cycles), + migrations.RunPython(populate_resources), migrations.RunPython(populate_projects), migrations.RunPython(populate_test_scheduling_set) ] diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py index f8cf8e2127b1f856ebc405a667b6500b2e8a529a..60b95bb0dff878b3b8eaa70606b9ad551eedcc71 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py @@ -150,6 +150,21 @@ class Flag(AbstractChoice): AUTOSCHEDULE = "allow_scheduling_observations" + +class Quantity(AbstractChoice): + """Defines the model and predefined list of possible period categories to be used in a.o. ProjectQuota and CycleQuota. + Please note that, by agreement, all values of a certain quantity are stored in SI units. + For example: a duration has the quantity "time" which is stored in "seconds". + We only store the quatity-type in the database, because we known the correct SI units for each quantity, so there is no need to store the unit as well. + The items in the Choices class below are automagically populated into the database via a data migration.""" + + class Choices(Enum): + # these are the basic quantities that we currently use. More can be added if needed. + TIME = "time" + BYTES = "bytes" + NUMBER = "number" + + class PeriodCategory(AbstractChoice): """Defines the model and predefined list of possible period categories to be used in Project. The items in the Choices class below are automagically populated into the database via a data migration.""" @@ -277,7 +292,7 @@ class Cycle(NamedCommonPK): class CycleQuota(Model): - cycle = ForeignKey('Cycle', related_name="cycle_quota", on_delete=PROTECT, help_text='Cycle to which these quota apply.') + cycle = ForeignKey('Cycle', related_name="quota", on_delete=PROTECT, help_text='Cycle to which these quota apply.') value = FloatField(help_text='Resource Quota value') resource_type = ForeignKey('ResourceType', on_delete=PROTECT, help_text='Resource type.') @@ -319,13 +334,13 @@ class Project(NamedCommonPK): class ProjectQuota(Model): - project = ForeignKey('Project', related_name="project_quota", on_delete=PROTECT, help_text='Project to wich this quota belongs.') # protected to avoid accidents + project = ForeignKey('Project', related_name="quota", on_delete=PROTECT, help_text='Project to wich this quota belongs.') # protected to avoid accidents value = FloatField(help_text='Resource Quota value') resource_type = ForeignKey('ResourceType', on_delete=PROTECT, help_text='Resource type.') # protected to avoid accidents class ResourceType(NamedCommonPK): - pass + quantity = ForeignKey('Quantity', null=False, on_delete=PROTECT, help_text='The quantity of this resource type.') class SchedulingSet(NamedCommon): diff --git a/SAS/TMSS/src/tmss/tmssapp/populate.py b/SAS/TMSS/src/tmss/tmssapp/populate.py index f847cfecc9dbbd0b987db974108d7c485510c0d3..65d405c019b3f15545677afe120e2de8c1c230c0 100644 --- a/SAS/TMSS/src/tmss/tmssapp/populate.py +++ b/SAS/TMSS/src/tmss/tmssapp/populate.py @@ -37,7 +37,7 @@ def populate_choices(apps, schema_editor): ''' for choice_class in [Role, Datatype, Dataformat, CopyReason, SubtaskState, SubtaskType, StationType, Algorithm, ScheduleMethod, SchedulingRelationPlacement, - Flag, ProjectCategory, PeriodCategory]: + Flag, ProjectCategory, PeriodCategory, Quantity]: choice_class.objects.bulk_create([choice_class(value=x.value) for x in choice_class.Choices]) def populate_settings(apps, schema_editor): @@ -207,7 +207,7 @@ def populate_cycles(apps, schema_editor): start=datetime(2013+nr//2, 6 if nr%2==0 else 11, 1, 0, 0, 0, 0, tzinfo=timezone.utc), stop=datetime(2013+(nr+1)//2, 6 if nr%2==1 else 11, 1, 0, 0, 0, 0, tzinfo=timezone.utc)) models.CycleQuota.objects.create(cycle=cycle, - resource_type=ResourceType.objects.get(name="lofar_observing_time"), + resource_type=ResourceType.objects.get(name="observing_time"), value=0.8*cycle.duration.total_seconds()) # rough guess. 80% of total time available for observing models.CycleQuota.objects.create(cycle=cycle, resource_type=ResourceType.objects.get(name="cep_processing_time"), @@ -219,13 +219,13 @@ def populate_cycles(apps, schema_editor): resource_type=ResourceType.objects.get(name="support_time"), value=0) # needs to be filled in by user (SOS) models.CycleQuota.objects.create(cycle=cycle, - resource_type=ResourceType.objects.get(name="lofar_observing_time_commissioning"), + resource_type=ResourceType.objects.get(name="observing_time_commissioning"), value=0.05*cycle.duration.total_seconds()) # rough guess. 5% of total time available for observing models.CycleQuota.objects.create(cycle=cycle, - resource_type=ResourceType.objects.get(name="lofar_observing_time_prio_a"), + resource_type=ResourceType.objects.get(name="observing_time_prio_a"), value=0) # needs to be filled in by user (SOS) models.CycleQuota.objects.create(cycle=cycle, - resource_type=ResourceType.objects.get(name="lofar_observing_time_prio_b"), + resource_type=ResourceType.objects.get(name="observing_time_prio_b"), value=0) # needs to be filled in by user (SOS) @@ -241,15 +241,15 @@ def populate_projects(apps, schema_editor): def populate_resources(apps, schema_editor): - ResourceType.objects.create(name="lta_storage", description="Amount of storage in the LTA (in bytes)") - ResourceType.objects.create(name="cep_storage", description="Amount of storage on the CEP processing cluster (in bytes)") - ResourceType.objects.create(name="cep_processing_time", description="Processing time on the CEP processing cluster (in seconds)") - ResourceType.objects.create(name="lofar_observing_time", description="Observing time (in seconds)") - ResourceType.objects.create(name="lofar_observing_time_prio_a", description="Observing time with priority A (in seconds)") - ResourceType.objects.create(name="lofar_observing_time_prio_b", description="Observing time with priority B (in seconds)") - ResourceType.objects.create(name="lofar_observing_time_commissioning", description="Observing time for Commissioning/DDT (in seconds)") - ResourceType.objects.create(name="support_time", description="Support time by human (in seconds)") - ResourceType.objects.create(name="number_of_triggers", description="Number of trigger events (as integer)") + ResourceType.objects.create(name="lta_storage", description="Amount of storage in the LTA (in bytes)", quantity=Quantity.objects.get(value=Quantity.Choices.BYTES.value)) + ResourceType.objects.create(name="cep_storage", description="Amount of storage on the CEP processing cluster (in bytes)", quantity=Quantity.objects.get(value=Quantity.Choices.BYTES.value)) + ResourceType.objects.create(name="cep_processing_time", description="Processing time on the CEP processing cluster (in seconds)", quantity=Quantity.objects.get(value=Quantity.Choices.TIME.value)) + ResourceType.objects.create(name="observing_time", description="Observing time (in seconds)", quantity=Quantity.objects.get(value=Quantity.Choices.TIME.value)) + ResourceType.objects.create(name="observing_time_prio_a", description="Observing time with priority A (in seconds)", quantity=Quantity.objects.get(value=Quantity.Choices.TIME.value)) + ResourceType.objects.create(name="observing_time_prio_b", description="Observing time with priority B (in seconds)", quantity=Quantity.objects.get(value=Quantity.Choices.TIME.value)) + ResourceType.objects.create(name="observing_time_commissioning", description="Observing time for Commissioning/DDT (in seconds)", quantity=Quantity.objects.get(value=Quantity.Choices.TIME.value)) + ResourceType.objects.create(name="support_time", description="Support time by human (in seconds)", quantity=Quantity.objects.get(value=Quantity.Choices.TIME.value)) + ResourceType.objects.create(name="number_of_triggers", description="Number of trigger events (as integer)", quantity=Quantity.objects.get(value=Quantity.Choices.NUMBER.value)) def populate_misc(apps, schema_editor): diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py index 4ca0e649e2336da9ef66ce25f3c01cdab2c7d872..f034bc0acc0376ba2725c25c28f04e5f4ce56d90 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py @@ -159,6 +159,12 @@ class DataformatSerializer(RelationalHyperlinkedModelSerializer): fields = '__all__' +class QuantitySerializer(RelationalHyperlinkedModelSerializer): + class Meta: + model = models.Quantity + fields = '__all__' + + class CopyReasonSerializer(RelationalHyperlinkedModelSerializer): class Meta: model = models.CopyReason @@ -177,7 +183,7 @@ class CycleSerializer(RelationalHyperlinkedModelSerializer): class Meta: model = models.Cycle fields = '__all__' - extra_fields = ['projects', 'name', 'duration', 'cycle_quota'] + extra_fields = ['projects', 'name', 'duration', 'quota'] class CycleQuotaSerializer(RelationalHyperlinkedModelSerializer): class Meta: @@ -191,7 +197,7 @@ class ProjectSerializer(RelationalHyperlinkedModelSerializer): class Meta: model = models.Project fields = '__all__' - extra_fields = ['name','project_quota'] #, 'scheduling_sets'] + extra_fields = ['name','quota'] #, 'scheduling_sets'] class ProjectQuotaSerializer(RelationalHyperlinkedModelSerializer): diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py index 8d36d92bf3fe7bffc2714bb1f27b9816594affc7..aa83ab8741ce9925bea917e6023c8af8bf8d3204 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py @@ -238,6 +238,11 @@ class SettingViewSet(LOFARViewSet): serializer_class = serializers.SettingSerializer +class QuantityViewSet(LOFARViewSet): + queryset = models.Quantity.objects.all() + serializer_class = serializers.QuantitySerializer + + class PeriodCategoryViewSet(LOFARViewSet): queryset = models.PeriodCategory.objects.all() serializer_class = serializers.PeriodCategorySerializer diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index 1e6109a40d54431db5ca0df830761382b3dbe443..6f9ea2167eb13acb6c3bf8dc330a07fe806881a2 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -89,6 +89,7 @@ router.register(r'copy_reason', viewsets.CopyReasonViewSet) router.register(r'flag', viewsets.FlagViewSet) router.register(r'period_category', viewsets.PeriodCategoryViewSet) router.register(r'project_category', viewsets.ProjectCategoryViewSet) +router.register(r'quantity', viewsets.QuantityViewSet) # templates router.register(r'generator_template', viewsets.GeneratorTemplateViewSet)