diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py index f8e2869a8dba9181d30cc682f8e9eaecebc23a9c..321d195c5d07befcb5a552e379868cf8aa098865 100755 --- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py +++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py @@ -965,8 +965,8 @@ class TestSkyConstraints(unittest.TestCase): self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -7201, 'to': -7199}} self.scheduling_unit_blueprint.specifications_doc['tasks']['Observation']['specifications_doc']['antenna_set'] = 'LBA_INNER' self.scheduling_unit_blueprint.specifications_doc['tasks']['Observation']['specifications_doc']['SAPs'] = \ - [{'name': 'CygA', 'target': 'CygA', 'subbands': [0, 1], 'digital_pointing': {'angle1': 5.233660650313663, 'angle2': 0.7109404782526458, 'direction_type': 'J2000'}}, - {'name': 'CasA', 'target': 'CasA', 'subbands': [2, 3], 'digital_pointing': {'angle1': 6.233660650313663, 'angle2': 0.6109404782526458, 'direction_type': 'J2000'}}] + [{'name': 'CygA', 'subbands': [0, 1], 'digital_pointing': {'angle1': 5.233660650313663, 'angle2': 0.7109404782526458, 'direction_type': 'J2000', 'target': 'CygA', }}, + {'name': 'CasA', 'subbands': [2, 3], 'digital_pointing': {'angle1': 6.233660650313663, 'angle2': 0.6109404782526458, 'direction_type': 'J2000', 'target': 'CasA',}}] self.scheduling_unit_blueprint.save() timestamp = datetime(2020, 1, 1, 12, 0, 0) returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration)) diff --git a/SAS/TMSS/backend/services/tmss_postgres_listener/lib/tmss_postgres_listener.py b/SAS/TMSS/backend/services/tmss_postgres_listener/lib/tmss_postgres_listener.py index cdc9bed5b68036ee2a12395766d7a70b8b0f7f90..a4d69679d087c524d6b071918912ae50f9480e48 100644 --- a/SAS/TMSS/backend/services/tmss_postgres_listener/lib/tmss_postgres_listener.py +++ b/SAS/TMSS/backend/services/tmss_postgres_listener/lib/tmss_postgres_listener.py @@ -210,8 +210,8 @@ class TMSSPGListener(PostgresListener): task_blueprint = subtask.task_blueprint task_id = subtask.task_blueprint.id task_cached_properties = {'status': task_blueprint.status, - 'start_time': task_blueprint.start_time, - 'stop_time': task_blueprint.stop_time } + 'scheduled_on_sky_start_time': task_blueprint.scheduled_on_sky_start_time, + 'scheduled_on_sky_stop_time': task_blueprint.scheduled_on_sky_stop_time} if task_id not in self._task_cache or self._task_cache[task_id][1] != task_cached_properties: # send generic updated event @@ -230,8 +230,8 @@ class TMSSPGListener(PostgresListener): scheduling_unit = task_blueprint.scheduling_unit_blueprint scheduling_unit_id = scheduling_unit.id scheduling_unit_cached_properties = {'status': scheduling_unit.status, - 'start_time': scheduling_unit.start_time, - 'stop_time': scheduling_unit.stop_time } + 'scheduled_on_sky_start_time': scheduling_unit.scheduled_on_sky_start_time, + 'scheduled_on_sky_stop_time': scheduling_unit.scheduled_on_sky_stop_time } if scheduling_unit_id not in self._scheduling_unit_cache or self._scheduling_unit_cache[scheduling_unit_id][1] != scheduling_unit_cached_properties: # send generic updated event diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/feedback.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/feedback.py index 9fdbd7d3d8053a1d6a58ed36301103f9c5f112bb..808f0cc55c235c2faa9ee483e36fba393aebd5f1 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/feedback.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/feedback.py @@ -93,6 +93,7 @@ def observation_correlated_feedback_to_feedback_doc(dp_feedback: dict) -> dict: "direction_type": dp_feedback['Pointing.directionType'], "angle1": float(dp_feedback['Pointing.angle1']), "angle2": float(dp_feedback['Pointing.angle2']), + "target": "Unknown" # todo } }, "samples": { @@ -138,6 +139,7 @@ def observation_beamformed_feedback_to_feedback_doc(dp_feedback: dict) -> dict: "direction_type": dp_feedback[beam_prefix + 'Pointing.directionType'], "angle1": float(dp_feedback[beam_prefix + 'Pointing.angle1']), "angle2": float(dp_feedback[beam_prefix + 'Pointing.angle2']), + "target": "Unknown" # todo }, "coherent": not beam_prefix.startswith("IncoherentStokesBeam") }, @@ -246,6 +248,7 @@ def pulsar_pipeline_analysis_feedback_to_feedback_doc(input_dp_feedback_doc: dic "direction_type": "J2000", "angle1": float(dp_feedback[beam_prefix + 'Pointing.angle1']), "angle2": float(dp_feedback[beam_prefix + 'Pointing.angle2']), + "target": input_dp_feedback_doc["target"]["pointing"]["target"] } if beam_prefix == "CoherentStokesBeam." else input_dp_feedback_doc["target"]["pointing"], "coherent": beam_prefix != "IncoherentStokesBeam." }, @@ -339,6 +342,9 @@ def process_feedback_into_subtask_dataproducts(subtask:Subtask, feedback: parame else: logger.error("cannot process feedback for pipelines with schema named %s", subtask.specifications_template.name) + # set value of dataproduct size field + dataproduct.size = dp_feedback.get('size', dataproduct.size) + # commit to database dataproduct.save() logger.info('saved processed feedback into dataproduct id=%s filename=%s feedback_doc=%s', dataproduct.id, dataproduct.filename, dataproduct.feedback_doc) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py index 6a4df77fcdbe180aba16e19e20ca36108b6a9db6..1bc70cc5e44ecbfca63943bf44d4ed6cccdc8737 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py @@ -343,7 +343,7 @@ def _convert_to_parset_dict_for_observationcontrol_schema(subtask: models.Subtas parset[beam_prefix+"directionType"] = digi_beam['pointing']['direction_type'] parset[beam_prefix+"angle1"] = digi_beam['pointing']['angle1'] parset[beam_prefix+"angle2"] = digi_beam['pointing']['angle2'] - parset[beam_prefix+"target"] = digi_beam['name'] + parset[beam_prefix+"target"] = digi_beam['pointing']['target'] parset[beam_prefix+"subbandList"] = digi_beam['subbands'] parset[beam_prefix+"nrTiedArrayBeams"] = 0 parset[beam_prefix+"nrTabRings"] = 0 diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/reports.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/reports.py index f50002cc86fe17169088942f2e58f596080ad1dc..6aa7bd92c9b5668d2a2f9520fc45678d2baf957a 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/reports.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/reports.py @@ -339,7 +339,7 @@ def create_project_report(request: Request, project: models.Project, start: date subs, durations = _get_subs_and_durations_from_project(project.pk, start, stop) result = {'project': project.pk, 'quota': _get_quotas_from_project(request, project.pk), 'SUBs': subs, 'durations': durations, 'LTA dataproducts': _get_lta_dataproducts(project.name, start, stop), - 'SAPs exposure': _get_saps_exposure(project.pk, start, stop),} + 'SAPs exposure': _get_saps_exposure(project.pk, start, stop), 'processing_resources': _get_processing_resources(project.pk, start, stop)} result['contains_overlapping_observations'] = any([issubclass(warning.category, TaskOverlappingWarning) for warning in w]) return result @@ -373,11 +373,11 @@ def _get_subs_and_durations_from_project(project_pk: int, start: datetime, stop: subs = project_subs.filter(priority_queue=prio.value) for sub in subs: # Check time interval and go to the next iteration (SUB) if the check fails - if (start and sub.start_time < start) or (stop and sub.stop_time > stop): + if (start and sub.scheduled_on_sky_start_time < start) or (stop and sub.scheduled_on_sky_stop_time > stop): continue # Gathering info for reporting - sub_start_time = sub.start_time.isoformat() if sub.start_time else None - sub_stop_time = sub.stop_time.isoformat() if sub.stop_time else None + sub_start_time = sub.scheduled_on_sky_start_time.isoformat() if sub.scheduled_on_sky_start_time else None + sub_stop_time = sub.scheduled_on_sky_stop_time.isoformat() if sub.scheduled_on_sky_stop_time else None sub_duration = sub.duration.total_seconds() sub_observed_duration = sub.observed_duration.total_seconds() if sub.observed_duration else 0 # Info about the SUB to be returned @@ -470,3 +470,24 @@ def _get_saps_exposure(project_pk: int, start: datetime, stop: datetime) -> {}: result[sap_name] += subtask.duration.total_seconds() return result + + +def _get_processing_resources(project_pk: int, start: datetime, stop: datetime) -> {}: + """ + Help function to retrieve info about processing resources. + """ + result = {'CPUtimeRAW': None, 'CPU_time_used': 0} + + # Get subtasks and filter basing on date interval if passed + subtasks = models.Subtask.objects.filter(task_blueprint__scheduling_unit_blueprint__draft__scheduling_set__project=project_pk) + subtasks = subtasks.filter(scheduled_on_sky_start_time__gte=start) if start else subtasks + subtasks = subtasks.filter(scheduled_on_sky_stop_time__lte=stop) if stop else subtasks + + # TODO: Update this accordingly when we'll have feedbacks. See TMSS-592. + pipeline_subtasks = subtasks.filter(specifications_template__type='pipeline') + for pipeline in pipeline_subtasks: + parallel_tasks = pipeline.specifications_doc.get('cluster_resources', {}).get('parallel_tasks', 0) + cores_per_task = pipeline.specifications_doc.get('cluster_resources', {}).get('cores_per_task', 0) + result['CPU_time_used'] += (pipeline.duration.total_seconds() * (parallel_tasks * cores_per_task) / 1000.0) + + return result diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py index f299db6d2fb0a7e8464777a3e71650281ee1bc30..fd30e6699297261f9de5a820c35333e998aa5d32 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py @@ -155,7 +155,7 @@ def create_sip_representation_for_subtask(subtask: Subtask): beamnumber=0, # TODO: where to get the beamnumber? identifier=get_siplib_identifier(sap.global_identifier, "SAP id=%s" % sap.id), measurementtype=sap.specifications_doc['measurement_type'].capitalize(), - targetname=sap.specifications_doc['target'], + targetname=sap.specifications_doc["pointing"]['target'], starttime=sap.specifications_doc['time']['start_time'], duration=isodate.duration_isoformat(datetime.timedelta(seconds=round(sap.specifications_doc['time']['duration']))), numberofprocessing=1, #TODO: check diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0007_systemevent.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0007_systemevent.py new file mode 100644 index 0000000000000000000000000000000000000000..0c9244dae2cdb26ba25dada3ed13c93facafe68e --- /dev/null +++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0007_systemevent.py @@ -0,0 +1,127 @@ +# Generated by Django 3.0.9 on 2021-10-18 14:36 + +from django.conf import settings +import django.contrib.postgres.fields +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tmssapp', '0006_subtask_obsolete_since'), + ] + + operations = [ + migrations.CreateModel( + name='SystemEvent', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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.')), + ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), + ('start', models.DateTimeField(help_text='When this event started.')), + ('stop', models.DateTimeField(help_text='When this event stopped (NULLable).', null=True)), + ('notes', models.CharField(help_text='Any additional information', max_length=255, null=True)), + ('affected_hardware_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Properties of this event, e.g. station list and other hardware affected')), + ('jira_url', models.CharField(help_text='Link to JIRA issue (if any)', max_length=255, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SystemEventSeverity', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SystemEventStatus', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SystemEventSubtype', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SystemEventTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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.')), + ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)), + ('description', models.CharField(blank=True, default='', help_text='A longer description of this object.', max_length=255)), + ('version', models.IntegerField(editable=False, help_text='Version of this template (with respect to other templates of the same name)')), + ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SystemEventType', + fields=[ + ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddConstraint( + model_name='systemeventtemplate', + constraint=models.UniqueConstraint(fields=('name', 'version'), name='systemeventtemplate_unique_name_version'), + ), + migrations.AddField( + model_name='systemevent', + name='affected_hardware_template', + field=models.ForeignKey(help_text='Template for the affected_hardware_doc.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SystemEventTemplate'), + ), + migrations.AddField( + model_name='systemevent', + name='affected_tasks', + field=models.ManyToManyField(blank=True, help_text='The task blueprints that are affected by this issue', related_name='system_events', to='tmssapp.TaskBlueprint'), + ), + migrations.AddField( + model_name='systemevent', + name='created_by', + field=models.ForeignKey(help_text='User who created this entry.', on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='systemevent', + name='issue_subtype', + field=models.ForeignKey(help_text='The subtype that classifies this issue', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SystemEventSubtype'), + ), + migrations.AddField( + model_name='systemevent', + name='issue_type', + field=models.ForeignKey(help_text='The main type that classifies this issue', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SystemEventType'), + ), + migrations.AddField( + model_name='systemevent', + name='severity', + field=models.ForeignKey(help_text='The subtype that classifies this issue', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SystemEventSeverity'), + ), + migrations.AddField( + model_name='systemevent', + name='status', + field=models.ForeignKey(help_text='The current status of this issue', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SystemEventStatus'), + ), + ] diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0008_populate_systemevent.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0008_populate_systemevent.py new file mode 100644 index 0000000000000000000000000000000000000000..276e0dada2da5f7f6aac8254fd429caf563c5f60 --- /dev/null +++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0008_populate_systemevent.py @@ -0,0 +1,14 @@ + + +from django.db import migrations + +from lofar.sas.tmss.tmss.tmssapp.populate import * + +class Migration(migrations.Migration): + + dependencies = [ + ('tmssapp', '0007_systemevent'), + ] + + operations = [migrations.RunPython(populate_system_event_choices)] + diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index f5d7828b5d11ad14b4657746b4d458bb7a489bf6..f0fce5f689b83743d5198a1b5aedea79c0571578 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -25,6 +25,7 @@ from lofar.sas.tmss.tmss.exceptions import BlueprintCreationException, TMSSExcep from django.db.models import Count from copy import deepcopy from lofar.common.util import subdict_of_pointer_items +from .permissions import TMSSUser as User # # I/O @@ -164,6 +165,52 @@ class ProjectState(AbstractChoice): SUSPENDED = "suspended" +class SystemEventType(AbstractChoice): + """Defines the model and predefined list of possible issue_types of a SystemEvent. + The items in the Choices class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + CEP = "cep" + COBALT = "cobalt" + ENVIRONMENT = "environment" + HUMAN ="human" + NETWORK = "network" + STATION = "station" + SYSTEM = "system" + OTHER = "other" + + +class SystemEventSubtype(AbstractChoice): + """Defines the model and predefined list of possible issue_subtypes of a SystemEvent. + The items in the Choices class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + CRASH = "crash" + DATALOSS = "dataloss" + HARDWARE = "hardware" + NOISY = "noisy" + OSCILLATING = "oscillating" + RFI = "rfi" + SETUP = "setup" + TEMPERATURE = "temperature" + OTHER = "other" + + +class SystemEventSeverity(AbstractChoice): + """Defines the model and predefined list of possible severity levels of a SystemEvent. + The items in the Choices class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + FAILURE = "failure" + MINOR = "minor" + MAJOR = "major" + + +class SystemEventStatus(AbstractChoice): + """Defines the model and predefined list of possible statuses of a SystemEvent. + The items in the Choices class below are automagically populated into the database via a data migration.""" + class Choices(Enum): + OPEN = "open" + ANALYSED = "analysed" + + # concrete models class Setting(BasicCommon): @@ -368,6 +415,9 @@ class ReservationStrategyTemplate(BaseStrategyTemplate): class ReservationTemplate(Template): pass +class SystemEventTemplate(Template): + pass + # # Instance Objects @@ -673,12 +723,12 @@ class SchedulingUnitBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, NamedCo @cached_property def duration(self) -> datetime.timedelta: - '''return the overall duration of all tasks of this scheduling unit + '''return the overall on sky duration of all tasks of this scheduling unit ''' - if self.start_time is None or self.stop_time is None: + if self.scheduled_on_sky_start_time is None or self.scheduled_on_sky_stop_time is None: return self.relative_stop_time - self.relative_start_time else: - return self.stop_time - self.start_time # <- todo: do we ever want this? + return self.scheduled_on_sky_stop_time - self.scheduled_on_sky_start_time # <- todo: do we ever want this? @cached_property def relative_start_time(self) -> datetime.timedelta: @@ -701,52 +751,152 @@ class SchedulingUnitBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, NamedCo return datetime.timedelta(seconds=0) @cached_property - def start_time(self) -> datetime or None: - '''return the earliest start time of all tasks of this scheduling unit + def scheduled_on_sky_start_time(self) -> datetime or None: + '''return the earliest scheduled on sky start time of all tasks of this scheduling unit + ''' + tasks_with_start_time = list(filter(lambda x: x.scheduled_on_sky_start_time is not None, self.task_blueprints.all())) + if tasks_with_start_time: + return min(tasks_with_start_time, key=lambda x: x.scheduled_on_sky_start_time).scheduled_on_sky_start_time + else: + return None + + @cached_property + def scheduled_on_sky_stop_time(self) -> datetime or None: + '''return the latest scheduled on sky stop time of all tasks of this scheduling unit + ''' + tasks_with_stop_time = list(filter(lambda x: x.scheduled_on_sky_stop_time is not None, self.task_blueprints.all())) + if tasks_with_stop_time: + return max(tasks_with_stop_time, key=lambda x: x.scheduled_on_sky_stop_time).scheduled_on_sky_stop_time + else: + return None + + @cached_property + def scheduled_process_start_time(self) -> datetime or None: + '''return the earliest scheduled process start time of all tasks of this scheduling unit + ''' + tasks_with_start_time = list(filter(lambda x: x.scheduled_process_start_time is not None, self.task_blueprints.all())) + if tasks_with_start_time: + return min(tasks_with_start_time, key=lambda x: x.scheduled_process_start_time).scheduled_process_start_time + else: + return None + + @cached_property + def scheduled_process_stop_time(self) -> datetime or None: + '''return the latest scheduled process stop time of all tasks of this scheduling unit + ''' + tasks_with_stop_time = list(filter(lambda x: x.scheduled_process_stop_time is not None, self.task_blueprints.all())) + if tasks_with_stop_time: + return max(tasks_with_stop_time, key=lambda x: x.scheduled_process_stop_time).scheduled_process_stop_time + else: + return None + + @cached_property + def actual_on_sky_start_time(self) -> datetime or None: + '''return the earliest actual on sky start time of all tasks of this scheduling unit + ''' + tasks_with_start_time = list(filter(lambda x: x.actual_on_sky_start_time is not None, self.task_blueprints.all())) + if tasks_with_start_time: + return min(tasks_with_start_time, key=lambda x: x.actual_on_sky_start_time).actual_on_sky_start_time + else: + return None + + @cached_property + def actual_on_sky_stop_time(self) -> datetime or None: + '''return the latest actual on sky stop time of all tasks of this scheduling unit + ''' + tasks_with_stop_time = list(filter(lambda x: x.actual_on_sky_stop_time is not None, self.task_blueprints.all())) + if tasks_with_stop_time: + return max(tasks_with_stop_time, key=lambda x: x.actual_on_sky_stop_time).actual_on_sky_stop_time + else: + return None + + @cached_property + def actual_process_start_time(self) -> datetime or None: + '''return the earliest actual process start time of all tasks of this scheduling unit ''' - tasks_with_start_time = list(filter(lambda x: x.start_time is not None, self.task_blueprints.all())) + tasks_with_start_time = list(filter(lambda x: x.actual_process_start_time is not None, self.task_blueprints.all())) if tasks_with_start_time: - return min(tasks_with_start_time, key=lambda x: x.start_time).start_time + return min(tasks_with_start_time, key=lambda x: x.actual_process_start_time).actual_process_start_time else: return None @cached_property - def stop_time(self) -> datetime or None: - '''return the latest stop time of all tasks of this scheduling unit + def actual_process_stop_time(self) -> datetime or None: + '''return the latest actual process stop time of all tasks of this scheduling unit ''' - tasks_with_stop_time = list(filter(lambda x: x.stop_time is not None, self.task_blueprints.all())) + tasks_with_stop_time = list(filter(lambda x: x.actual_process_stop_time is not None, self.task_blueprints.all())) if tasks_with_stop_time: - return max(tasks_with_stop_time, key=lambda x: x.stop_time).stop_time + return max(tasks_with_stop_time, key=lambda x: x.actual_process_stop_time).actual_process_stop_time + else: + return None + + @cached_property + def on_sky_start_time(self) -> datetime or None: + '''return the earliest on sky start time of all the observation tasks of this scheduling unit. + ''' + observation_tasks_with_start_time = list(filter(lambda x: (x.specifications_template.type.value == TaskType.Choices.OBSERVATION.value and x.on_sky_start_time is not None), self.task_blueprints.all())) + if observation_tasks_with_start_time: + return min(observation_tasks_with_start_time, key=lambda x: x.on_sky_start_time).on_sky_start_time + else: + return None + + @cached_property + def on_sky_stop_time(self) -> datetime or None: + '''return the latest on sky stop time of all the observation tasks of this scheduling unit. + ''' + observation_tasks_with_stop_time = list(filter(lambda x: (x.specifications_template.type.value == TaskType.Choices.OBSERVATION.value and x.on_sky_stop_time is not None), self.task_blueprints.all())) + if observation_tasks_with_stop_time: + return max(observation_tasks_with_stop_time, key=lambda x: x.on_sky_stop_time).on_sky_stop_time + else: + return None + + @cached_property + def process_start_time(self) -> datetime or None: + '''return the earliest process start time of all the tasks of this scheduling unit. + ''' + tasks_with_start_time = list(filter(lambda x: x.process_start_time is not None, self.task_blueprints.all())) + if tasks_with_start_time: + return min(tasks_with_start_time, key=lambda x: x.process_start_time).process_start_time + else: + return None + + @cached_property + def process_stop_time(self) -> datetime or None: + '''return the latest process stop time of all the tasks of this scheduling unit. + ''' + tasks_with_stop_time = list(filter(lambda x: x.process_stop_time is not None, self.task_blueprints.all())) + if tasks_with_stop_time: + return max(tasks_with_stop_time, key=lambda x: x.process_stop_time).process_stop_time else: return None @property def observed_start_time(self) -> datetime or None: """ - return the earliest start time of all (observation) tasks of this scheduling unit with the status observed/finished + return the earliest on sky start time of all (observation) tasks of this scheduling unit with the status observed/finished """ observed_tasks = [] for task in self.task_blueprints.all(): if task.specifications_template.type.value == TaskType.Choices.OBSERVATION.value and \ - (task.status == "observed" or task.status == "finished") and task.start_time is not None: + (task.status == "observed" or task.status == "finished") and task.scheduled_on_sky_start_time is not None: observed_tasks.append(task) if observed_tasks: - return min(observed_tasks, key=lambda x: x.start_time).start_time + return min(observed_tasks, key=lambda x: x.scheduled_on_sky_start_time).scheduled_on_sky_start_time else: return None @property def observed_end_time(self) -> datetime or None: """ - return the latest stop time of all (observation) tasks of this scheduling unit with the status observed/finished + return the latest on sky stop time of all (observation) tasks of this scheduling unit with the status observed/finished """ observed_tasks = [] for task in self.task_blueprints.all(): if task.specifications_template.type.value == TaskType.Choices.OBSERVATION.value and\ - (task.status == "observed" or task.status == "finished") and task.stop_time is not None: + (task.status == "observed" or task.status == "finished") and task.scheduled_on_sky_stop_time is not None: observed_tasks.append(task) if observed_tasks: - return max(observed_tasks, key=lambda x: x.stop_time).stop_time + return max(observed_tasks, key=lambda x: x.scheduled_on_sky_stop_time).scheduled_on_sky_stop_time else: return None @@ -1016,6 +1166,13 @@ class SchedulingUnitBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, NamedCo return fields_found + @property + def system_events(self): + events = [] + for task in self.task_blueprints.all(): + events += task.system_events + return set(events) + class TaskDraft(NamedCommon, TemplateSchemaMixin, ProjectPropertyMixin): short_description = CharField(max_length=32, help_text='A short description of this task, usually the name of the target and abbreviated task type.', blank=True, default="") @@ -1199,10 +1356,10 @@ class TaskBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, RefreshFromDbInva def duration(self) -> datetime.timedelta: '''return the overall duration of this task ''' - if self.start_time is None or self.stop_time is None: + if self.scheduled_on_sky_start_time is None or self.scheduled_on_sky_stop_time is None: return self.relative_stop_time - self.relative_start_time else: - return self.stop_time - self.start_time + return self.scheduled_on_sky_stop_time - self.schedule_on_sky_start_time @cached_property def relative_start_time(self) -> datetime.timedelta: @@ -1246,8 +1403,8 @@ class TaskBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, RefreshFromDbInva return self.relative_start_time @cached_property - def start_time(self) -> datetime or None: - '''return the earliest start time of all subtasks of this task + def scheduled_on_sky_start_time(self) -> datetime or None: + '''return the earliest scheduled on sky start time of all subtasks of this task ''' subtasks_with_start_time = list(filter(lambda x: x.scheduled_on_sky_start_time is not None, self.subtasks.all())) if subtasks_with_start_time: @@ -1256,8 +1413,8 @@ class TaskBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, RefreshFromDbInva return None @cached_property - def stop_time(self) -> datetime or None: - '''return the latest stop time of all subtasks of this task + def scheduled_on_sky_stop_time(self) -> datetime or None: + '''return the latest scheduled on sky stop time of all subtasks of this task ''' subtasks_with_stop_time = list(filter(lambda x: x.scheduled_on_sky_stop_time is not None, self.subtasks.all())) if subtasks_with_stop_time: @@ -1265,6 +1422,106 @@ class TaskBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, RefreshFromDbInva else: return None + @cached_property + def scheduled_process_start_time(self) -> datetime or None: + '''return the earliest scheduled process start time of all subtasks of this task + ''' + subtasks_with_start_time = list(filter(lambda x: x.scheduled_process_start_time is not None, self.subtasks.all())) + if subtasks_with_start_time: + return min(subtasks_with_start_time, key=lambda x: x.scheduled_process_start_time).scheduled_process_start_time + else: + return None + + @cached_property + def scheduled_process_stop_time(self) -> datetime or None: + '''return the latest scheduled process stop time of all subtasks of this task + ''' + subtasks_with_stop_time = list(filter(lambda x: x.scheduled_process_stop_time is not None, self.subtasks.all())) + if subtasks_with_stop_time: + return max(subtasks_with_stop_time, key=lambda x: x.scheduled_process_stop_time).scheduled_process_stop_time + else: + return None + + @cached_property + def actual_on_sky_start_time(self) -> datetime or None: + '''return the earliest actual on sky start time of all subtasks of this task + ''' + subtasks_with_start_time = list(filter(lambda x: x.actual_on_sky_start_time is not None, self.subtasks.all())) + if subtasks_with_start_time: + return min(subtasks_with_start_time, key=lambda x: x.actual_on_sky_start_time).actual_on_sky_start_time + else: + return None + + @cached_property + def actual_on_sky_stop_time(self) -> datetime or None: + '''return the latest actual on sky stop time of all subtasks of this task + ''' + subtasks_with_stop_time = list(filter(lambda x: x.actual_on_sky_stop_time is not None, self.subtasks.all())) + if subtasks_with_stop_time: + return max(subtasks_with_stop_time, key=lambda x: x.actual_on_sky_stop_time).actual_on_sky_stop_time + else: + return None + + @cached_property + def actual_process_start_time(self) -> datetime or None: + '''return the earliest actual process start time of all subtasks of this task + ''' + subtasks_with_start_time = list(filter(lambda x: x.actual_process_start_time is not None, self.subtasks.all())) + if subtasks_with_start_time: + return min(subtasks_with_start_time, key=lambda x: x.actual_process_start_time).actual_process_start_time + else: + return None + + @cached_property + def actual_process_stop_time(self) -> datetime or None: + '''return the latest actual process stop time of all subtasks of this task + ''' + subtasks_with_stop_time = list(filter(lambda x: x.actual_process_stop_time is not None, self.subtasks.all())) + if subtasks_with_stop_time: + return max(subtasks_with_stop_time, key=lambda x: x.actual_process_stop_time).actual_process_stop_time + else: + return None + + @cached_property + def on_sky_start_time(self) -> datetime or None: + '''return the earliest on sky start time of all the observation subtasks of this task. + ''' + observation_control_subtasks = list(filter(lambda x: (x.specifications_template.name == 'observation control' and x.on_sky_start_time is not None), self.subtasks.all())) + if observation_control_subtasks: + return min(observation_control_subtasks, key=lambda x: x.on_sky_start_time).on_sky_start_time + else: + return None + + @cached_property + def on_sky_stop_time(self) -> datetime or None: + '''return the latest on sky stop time of all the observation subtasks of this task. + ''' + observation_control_subtasks = list(filter(lambda x: (x.specifications_template.name == 'observation control' and x.on_sky_stop_time is not None), self.subtasks.all())) + if observation_control_subtasks: + return max(observation_control_subtasks, key=lambda x: x.on_sky_stop_time).on_sky_stop_time + else: + return None + + @cached_property + def process_start_time(self) -> datetime or None: + '''return the earliest process start time of all the subtasks of this task. + ''' + subtasks_with_start_time = list(filter(lambda x: x.process_start_time is not None, self.subtasks.all())) + if subtasks_with_start_time: + return min(subtasks_with_start_time, key=lambda x: x.process_start_time).process_start_time + else: + return None + + @cached_property + def process_stop_time(self) -> datetime or None: + '''return the latest process stop time of all the subtasks of this task. + ''' + subtasks_with_stop_time = list(filter(lambda x: x.process_stop_time is not None, self.subtasks.all())) + if subtasks_with_stop_time: + return max(subtasks_with_stop_time, key=lambda x: x.process_stop_time).process_stop_time + else: + return None + @cached_property def is_obsolete(self) -> bool: '''is this task obsolete (because any of its subtasks are obsolete)''' @@ -1438,3 +1695,17 @@ class Reservation(NamedCommon, TemplateSchemaMixin): self.annotate_validate_add_defaults_to_doc_using_template('specifications_doc', 'specifications_template') super().save(force_insert, force_update, using, update_fields) + +class SystemEvent(NamedCommon): + created_by = ForeignKey(User, null=False, on_delete=PROTECT, help_text='User who created this entry.') + start = DateTimeField(help_text='When this event started.') + stop = DateTimeField(null=True, help_text='When this event stopped (NULLable).') + issue_type = ForeignKey('SystemEventType', on_delete=PROTECT, help_text='The main type that classifies this issue') + issue_subtype = ForeignKey('SystemEventSubtype', on_delete=PROTECT, help_text='The subtype that classifies this issue') + severity = ForeignKey('SystemEventSeverity', on_delete=PROTECT, help_text='The subtype that classifies this issue') + notes = CharField(max_length=255, null=True, help_text='Any additional information') + status = ForeignKey('SystemEventStatus', on_delete=PROTECT, help_text='The current status of this issue') + affected_hardware_doc = JSONField(help_text='Properties of this event, e.g. station list and other hardware affected') + affected_hardware_template = ForeignKey('SystemEventTemplate', on_delete=PROTECT, help_text='Template for the affected_hardware_doc.') + affected_tasks = ManyToManyField('TaskBlueprint', blank=True, related_name='system_events', help_text='The task blueprints that are affected by this issue') + jira_url = CharField(max_length=255, null=True, help_text='Link to JIRA issue (if any)') \ No newline at end of file diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py index 011c952ba5984ae2ef961820802e67a82fc89dee..6665dee08d582240a975f75abdd5ab1293f94d73 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py @@ -134,6 +134,19 @@ def populate_choices(apps, schema_editor): executor.map(lambda choice_class: choice_class.objects.bulk_create([choice_class(value=x.value) for x in choice_class.Choices]), choice_classes) +def populate_system_event_choices(apps, schema_editor): + ''' + populate each 'choice' table in the database with the 'static' list of 'choice'.Choices for + each SystemEvent-related 'choice'type that has been added in a later migration than the initial populate. + :return: None + ''' + choice_classes = [SystemEventType, SystemEventSubtype, SystemEventSeverity, SystemEventStatus] + + # upload choices in parallel + with ThreadPoolExecutor() as executor: + executor.map(lambda choice_class: choice_class.objects.bulk_create([choice_class(value=x.value) for x in choice_class.Choices]), + choice_classes) + def populate_subtask_allowed_state_transitions(apps, schema_editor): '''populate the SubtaskAllowedStateTransitions table with the allowed state transitions as defined by the design in https://support.astron.nl/confluence/display/TMSS/Subtask+State+Machine''' DEFINING = SubtaskState.objects.get(value=SubtaskState.Choices.DEFINING.value) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/pointing-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/pointing-1.json index 95ccd3d9fc4acaf53d8fc49e7fd752ed176907f4..2c08c09b3698434b8e373d4baa9545d4849e256c 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/pointing-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/pointing-1.json @@ -37,11 +37,19 @@ ], "title": "Reference frame", "type": "string" + }, + "target": { + "default": "_target_name_", + "description": "Description of where this beam points at", + "minLength": 1, + "title": "Target", + "type": "string" } }, "required": [ "angle1", - "angle2" + "angle2", + "target" ], "type": "object" } diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/stations-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/stations-1.json index 04b8adfc92efeaa4cd7d2d19a5603167c78f457d..ea83e354530957933c3f42ee38bdbdf4dd53acca 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/stations-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/stations-1.json @@ -39,17 +39,9 @@ }, "title": "Subband list", "type": "array" - }, - "target": { - "default": "_target_name_", - "description": "Description of where this beam points at", - "minLength": 1, - "title": "Target", - "type": "string" } }, "required": [ - "target", "name", "digital_pointing", "subbands" diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Beamforming_(Complex_Voltages)_Observation-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Beamforming_(Complex_Voltages)_Observation-1.json index fc44853d223b2c1de8ca2d8bb2daa8c1f9d248f7..6baf4aff5978b78d4c0e68ee77c7bf7c8af8c01f 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Beamforming_(Complex_Voltages)_Observation-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Beamforming_(Complex_Voltages)_Observation-1.json @@ -82,7 +82,8 @@ "digital_pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "SAP0", "subbands": [ @@ -486,8 +487,7 @@ 448, 449, 450 - ], - "target": "B0329+54" + ] } ], "antenna_set": "HBA_DUAL_INNER", @@ -911,7 +911,8 @@ "pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "relative": false } @@ -952,7 +953,8 @@ "tile_beam": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/HBA_single_beam_imaging-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/HBA_single_beam_imaging-1.json index 0c783a1f91874794a7276257a37d050740b04b1a..13072a37756baab29c06a1394446c57a651b965b 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/HBA_single_beam_imaging-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/HBA_single_beam_imaging-1.json @@ -221,7 +221,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { @@ -238,7 +239,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { @@ -325,7 +327,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "target", "subbands": [ @@ -897,7 +900,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/IM_LBA_Survey_Strategy_-_3_Beams-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/IM_LBA_Survey_Strategy_-_3_Beams-1.json index 400a3eaa1a738bae4c789ee6114c1a144e8264ac..cfbc92e551fe5e000d3d57556ed7d963582dd7ee 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/IM_LBA_Survey_Strategy_-_3_Beams-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/IM_LBA_Survey_Strategy_-_3_Beams-1.json @@ -256,7 +256,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "target": { @@ -278,7 +279,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "target1", "subbands": [ @@ -410,7 +412,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "target2", "subbands": [ @@ -542,7 +545,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "target3", "subbands": [ diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/LoTSS_Observing_strategy-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/LoTSS_Observing_strategy-1.json index 029032ab24cc09587bb6e2d0d2546f3d02bc5417..99a21f92089d612bef4f40b2b2ec6e3e5d3cea61 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/LoTSS_Observing_strategy-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/LoTSS_Observing_strategy-1.json @@ -253,7 +253,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" } }, "specifications_template": { @@ -270,7 +271,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" } }, "specifications_template": { @@ -407,7 +409,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" }, "name": "target1", "subbands": [ @@ -660,7 +663,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" }, "name": "target2", "subbands": [ @@ -992,7 +996,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Pulsar_timing-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Pulsar_timing-1.json index 099a2e3e76131793d73cdcfbc70453db8b2e9892..375899c8addc7320295ae531de381b0d6b0b707c 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Pulsar_timing-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Pulsar_timing-1.json @@ -175,7 +175,8 @@ "digital_pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "B0329+54", "subbands": [ @@ -579,8 +580,7 @@ 448, 449, 450 - ], - "target": "B0329+54" + ] } ], "antenna_set": "HBA_DUAL_INNER", @@ -1004,7 +1004,8 @@ "pointing": { "angle1": 0, "angle2": 0, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "relative": true } @@ -1058,7 +1059,8 @@ "tile_beam": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Responsive_Telescope_HBA_LoTSS-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Responsive_Telescope_HBA_LoTSS-1.json index e8a7616a7f5b6ad2dcf77493247cceb03a699c9a..f6d8ca5fe91f4834fe9508b6c7b8d90a2d7e31a6 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Responsive_Telescope_HBA_LoTSS-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Responsive_Telescope_HBA_LoTSS-1.json @@ -173,7 +173,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" } }, "specifications_template": { @@ -235,7 +236,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" }, "name": "target", "subbands": [ @@ -542,7 +544,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "PXXX+YY" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Beamformed_Observation_-_Pipeline_-_Ingest-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Beamformed_Observation_-_Pipeline_-_Ingest-1.json index fe88037d2123834307d3a9c919e6f2269444fbb4..b0b78c52a629653237b71720926a6437b548db22 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Beamformed_Observation_-_Pipeline_-_Ingest-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Beamformed_Observation_-_Pipeline_-_Ingest-1.json @@ -169,7 +169,8 @@ "digital_pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "B0329+54", "subbands": [ @@ -417,8 +418,7 @@ 241, 242, 243 - ], - "target": "B0329+54" + ] } ], "antenna_set": "HBA_DUAL_INNER", @@ -686,7 +686,8 @@ "pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "relative": false } @@ -770,7 +771,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Observation_-_Pipeline_-_Ingest-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Observation_-_Pipeline_-_Ingest-1.json index 3acf4a21b09417a21caa65a7c2df52347f25c5d3..f8ae5efe1ca11c2c6fb42a8a7c2c02082840ff93 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Observation_-_Pipeline_-_Ingest-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Short_Test_Observation_-_Pipeline_-_Ingest-1.json @@ -156,7 +156,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "Polaris", "subbands": [ @@ -404,8 +405,7 @@ 241, 242, 243 - ], - "target": "Polaris" + ] } ], "antenna_set": "HBA_DUAL_INNER", @@ -432,7 +432,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Beamforming_Observation-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Beamforming_Observation-1.json index 8a35b71927ce970da76a00f9ce5ff3153f690600..6baa06767c3d53d7f336abc9457bfb90ab4e58a8 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Beamforming_Observation-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Beamforming_Observation-1.json @@ -64,7 +64,8 @@ "digital_pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "B0329+54", "subbands": [ @@ -312,8 +313,7 @@ 241, 242, 243 - ], - "target": "B0329+54" + ] } ], "antenna_set": "HBA_DUAL_INNER", @@ -581,7 +581,8 @@ "pointing": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "relative": false } @@ -665,7 +666,8 @@ "tile_beam": { "angle1": 0.92934186635, "angle2": 0.952579228492, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Observation-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Observation-1.json index 560670961e6fee0508462f4bbfe51bc7237062a4..3b1125376357c6ffd307d99c6e5a752b349635d9 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Observation-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/Simple_Observation-1.json @@ -71,7 +71,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "Polaris", "subbands": [ @@ -319,8 +320,7 @@ 241, 242, 243 - ], - "target": "Polaris" + ] } ], "antenna_set": "HBA_DUAL_INNER", @@ -347,7 +347,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/UC1_CTC+pipelines-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/UC1_CTC+pipelines-1.json index f7b7579125c1733d7712c1c23b3915b219acec72..1ff3c1500868c4345b4df6b9922704b054bce4e1 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/UC1_CTC+pipelines-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/scheduling_unit_observing_strategy_template/UC1_CTC+pipelines-1.json @@ -211,7 +211,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { @@ -228,7 +229,8 @@ "pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { @@ -365,7 +367,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "target1", "subbands": [ @@ -619,7 +622,8 @@ "digital_pointing": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" }, "name": "target2", "subbands": [ @@ -952,7 +956,8 @@ "tile_beam": { "angle1": 0.6624317181687094, "angle2": 1.5579526427549426, - "direction_type": "J2000" + "direction_type": "J2000", + "target": "target1" } }, "specifications_template": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/subtask_template/observation/observation_control-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/subtask_template/observation/observation_control-1.json index 2b4596171b2338e0d748eeb720bd6fb95fadfaf1..fa47823f4df2cbb9d1848d5b3f8b157c1735d2c6 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/subtask_template/observation/observation_control-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/subtask_template/observation/observation_control-1.json @@ -313,13 +313,6 @@ }, "title": "Subband list", "type": "array" - }, - "target": { - "default": "_target_name_", - "description": "Name of the target", - "minLength": 1, - "title": "Target", - "type": "string" } }, "required": [ diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/affectedhardware-1.json b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/system_event_template/affectedhardware-1.json similarity index 90% rename from SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/affectedhardware-1.json rename to SAS/TMSS/backend/src/tmss/tmssapp/schemas/system_event_template/affectedhardware-1.json index c616feeecca18defca10051e11bc36c7406a31ad..8d89e603e422b37e8920d7beb0d92f3383632481 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/schemas/common_schema_template/affectedhardware-1.json +++ b/SAS/TMSS/backend/src/tmss/tmssapp/schemas/system_event_template/affectedhardware-1.json @@ -1,5 +1,5 @@ { - "$id": "http://127.0.0.1:8000/api/schemas/commonschematemplate/affectedhardware/1#", + "$id": "http://127.0.0.1:8000/api/schemas/systemeventtemplate/affectedhardware/1#", "$schema": "http://json-schema.org/draft-06/schema#", "description": "This schema defines the hardware that was affected by a system event.", "properties": { diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py index d17bf85c214e9cf38e3167fd278e9ddc415d966e..d08254939dcb3a0acc09173a8d376aeb3570726a 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py @@ -249,7 +249,10 @@ class SchedulingUnitBlueprintSerializer(DynamicRelationalHyperlinkedModelSeriali model = models.SchedulingUnitBlueprint fields = '__all__' read_only_fields = ['interrupts_telescope'] - extra_fields = ['task_blueprints', 'duration', 'start_time', 'stop_time', 'status', 'observed_end_time', 'output_pinned'] + extra_fields = ['task_blueprints', 'duration', 'status', 'observed_end_time', 'output_pinned', + 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time', + 'scheduled_on_sky_start_time', 'scheduled_on_sky_stop_time', 'scheduled_process_start_time', 'scheduled_process_stop_time', + 'actual_on_sky_start_time', 'actual_on_sky_stop_time', 'actual_process_start_time', 'actual_process_stop_time'] expandable_fields = { 'specifications_template': 'lofar.sas.tmss.tmss.tmssapp.serializers.SchedulingUnitTemplateSerializer', 'draft': 'lofar.sas.tmss.tmss.tmssapp.serializers.SchedulingUnitDraftSerializer', @@ -290,8 +293,10 @@ class TaskBlueprintSerializer(DynamicRelationalHyperlinkedModelSerializer): model = models.TaskBlueprint fields = '__all__' extra_fields = ['subtasks', 'produced_by', 'consumed_by', 'first_scheduling_relation', 'second_scheduling_relation', 'duration', - 'start_time', 'stop_time', 'relative_start_time', 'relative_stop_time', 'status', 'task_type', - 'obsolete_since'] + 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time', + 'scheduled_on_sky_start_time', 'scheduled_on_sky_stop_time', 'scheduled_process_start_time', 'scheduled_process_stop_time', + 'actual_on_sky_start_time', 'actual_on_sky_stop_time', 'actual_process_start_time', 'actual_process_stop_time', + 'relative_start_time', 'relative_stop_time', 'status', 'task_type', 'obsolete_since'] expandable_fields = { 'draft': 'lofar.sas.tmss.tmss.tmssapp.serializers.TaskDraftSerializer', 'scheduling_unit_blueprint': 'lofar.sas.tmss.tmss.tmssapp.serializers.SchedulingUnitBlueprintSerializer', @@ -404,3 +409,47 @@ class ProjectStateSerializer(DynamicRelationalHyperlinkedModelSerializer): class Meta: model = models.ProjectState fields = '__all__' + + +class SystemEventTypeSerializer(DynamicRelationalHyperlinkedModelSerializer): + + class Meta: + model = models.SystemEventType + fields = '__all__' + + +class SystemEventSubtypeSerializer(DynamicRelationalHyperlinkedModelSerializer): + + class Meta: + model = models.SystemEventSubtype + fields = '__all__' + + +class SystemEventSeveritySerializer(DynamicRelationalHyperlinkedModelSerializer): + + class Meta: + model = models.SystemEventSeverity + fields = '__all__' + + +class SystemEventStatusSerializer(DynamicRelationalHyperlinkedModelSerializer): + + class Meta: + model = models.SystemEventStatus + fields = '__all__' + + +class SystemEventTemplateSerializer(AbstractTemplateSerializer): + + class Meta: + model = models.SystemEventTemplate + fields = '__all__' + + +class SystemEventSerializer(DynamicRelationalHyperlinkedModelSerializer): + affected_hardware_doc = JSONEditorField(schema_source='affected_hardware_template.schema') + created_by = UserSerializer(required=False) + + class Meta: + model = models.SystemEvent + fields = '__all__' \ No newline at end of file diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py index 68f849f77d160de4b52132961557dee6a82b7835..2ac41109773f9e3f823e3b6602f765774b2aa1e2 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py @@ -111,11 +111,13 @@ def _filter_subbands(obs_subbands: list, selection: dict) -> [int]: raise SubtaskCreationException('Did not get a valid subband selection: got %s' % selection["method"]) -def _add_pointings(pointing_a, pointing_b): +def _add_pointings(pointing_a, pointing_b, target=None): if pointing_a['direction_type'] != pointing_b['direction_type']: raise SubtaskCreationException( "Cannot add pointings because direction types differ pointing_a=%s; pointing_b=%s" % (pointing_a, pointing_b)) - pointing = {"direction_type": pointing_a['direction_type']} + if target is None: + target = pointing_a["target"] if pointing_a["target"] == pointing_b["target"] else "%s_+_%s" % (pointing_a["target"], pointing_b["target"]) + pointing = {"direction_type": pointing_a['direction_type'], "target": target} for angle in ['angle1', 'angle2']: pointing[angle] = pointing_a.get(angle, 0.0) + pointing_b.get(angle, 0.0) return pointing @@ -133,10 +135,16 @@ def _generate_tab_ring_pointings(pointing, tab_rings) -> [dict]: width=tab_rings['width'], center=(pointing['angle1'], pointing['angle2']), dirtype=pointing['direction_type']).coordinates() - relative_pointings = [{'angle1': angle1, 'angle2': angle2, 'direction_type': pointing['direction_type']} for angle1, angle2 in coordinates] # add ring coordinates to main pointing to get absolute TAB pointings and return them - tab_pointings = [_add_pointings(pointing, relative_pointing) for relative_pointing in relative_pointings] + tab_pointings = [] + tab_nr = 0 + for angle1, angle2 in coordinates: + target = "TAB %s around %s" % (tab_nr, pointing.get('target', "(%s,%s)" % (pointing['angle1'], pointing['angle2']))) + relative_pointing = {'angle1': angle1, 'angle2': angle2, 'direction_type': pointing['direction_type'], 'target': target} + tab_pointings.append(_add_pointings(pointing, relative_pointing, target=target)) + tab_nr += 1 + return tab_pointings @@ -434,17 +442,18 @@ def create_observation_subtask_specifications_from_observation_task_blueprint(ta for sap in target_task_spec.get("SAPs", []): subtask_spec['stations']['digital_pointings'].append( {"name": sap["name"], - "target": sap["target"], "pointing": {"direction_type": sap["digital_pointing"]["direction_type"], "angle1": sap["digital_pointing"]["angle1"], - "angle2": sap["digital_pointing"]["angle2"]}, + "angle2": sap["digital_pointing"]["angle2"], + "target": sap["digital_pointing"]["target"]}, "subbands": sap["subbands"] }) if "tile_beam" in target_task_spec: subtask_spec['stations']['analog_pointing'] = { "direction_type": target_task_spec["tile_beam"]["direction_type"], "angle1": target_task_spec["tile_beam"]["angle1"], - "angle2": target_task_spec["tile_beam"]["angle2"] } + "angle2": target_task_spec["tile_beam"]["angle2"], + "target": target_task_spec["tile_beam"]["target"]} @@ -464,7 +473,8 @@ def create_observation_subtask_specifications_from_observation_task_blueprint(ta subtask_spec['stations']['analog_pointing'] = { "direction_type": target_task_spec["tile_beam"]["direction_type"], "angle1": target_task_spec["tile_beam"]["angle1"], - "angle2": target_task_spec["tile_beam"]["angle2"]} + "angle2": target_task_spec["tile_beam"]["angle2"], + "target": target_task_spec["tile_beam"]["target"]} else: raise SubtaskCreationException("Cannot determine the pointing specification from task_blueprint " "id=%s in auto-select mode, because the related target observation " @@ -473,7 +483,8 @@ def create_observation_subtask_specifications_from_observation_task_blueprint(ta else: subtask_spec['stations']['analog_pointing'] = {"direction_type": calibrator_task_spec["pointing"]["direction_type"], "angle1": calibrator_task_spec["pointing"]["angle1"], - "angle2": calibrator_task_spec["pointing"]["angle2"]} + "angle2": calibrator_task_spec["pointing"]["angle2"], + "target": calibrator_task_spec["pointing"]["target"]} # for the calibrator, the subbands are the union of the subbands of the targetobs subbands = [] diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py index 75935d1ee3134c23085a5cf9baabaf2d342e773e..356038158519f251c6083fcaf221d8ec9404f532 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py @@ -584,11 +584,36 @@ class ProjectCategoryViewSet(LOFARViewSet): class NumberInFilter(filters.BaseInFilter, filters.NumberFilter): """ - Custom filter for comma-separated lists of numbers + Custom filter that accepts a comma-separated lists of numbers. + Returns objects where the (single) numeric value of the filtered field is in the provided list. """ pass +class CharInFilter(filters.BaseInFilter, filters.CharFilter): + """ + Custom filter that accepts a comma-separated lists of numbers. + Filters for objects where the (single) numeric value of the filtered field is in the provided list. + """ + pass + + +class ListContainsAnyFilter(filters.BaseCSVFilter): + """ + Custom filter for where both the filtered field and the filter input are comma-separated lists. + Filters for objects where any of the values in the provided list is contained in the filtered field. + """ + def filter(self, qs, values): + from django.db.models import Q + query = Q() + if not values: + return qs + else: + for value in values: + query |= Q(**{self.field_name+'__contains': value.strip()}) + return qs.filter(query) + + class FloatRangeField(filter_fields.RangeField): """ Custom range field for float values. The normal django filter_fields.RangeField uses (integer) decimal fields. @@ -815,8 +840,18 @@ class ModelChoiceInFilter(filters.BaseInFilter, filters.ModelChoiceFilter): pass class SchedulingUnitBlueprintPropertyFilter(property_filters.PropertyFilterSet): - start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='start_time') - stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='stop_time') + on_sky_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='on_sky_start_time') + on_sky_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='on_sky_stop_time') + process_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='process_start_time') + process_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='process_stop_time') + scheduled_on_sky_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_on_sky_start_time') + scheduled_on_sky_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_on_sky_stop_time') + scheduled_process_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_process_start_time') + scheduled_process_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_process_stop_time') + actual_on_sky_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_on_sky_start_time') + actual_on_sky_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_on_sky_stop_time') + actual_process_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_process_start_time') + actual_process_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_process_stop_time') project = property_filters.PropertyCharFilter(field_name='project', lookup_expr='icontains') status = property_filters.PropertyMultipleChoiceFilter(field_name='status', choices=tuple((i.value, i.value) for i in models.SchedulingUnitBlueprint.Status), lookup_expr='iexact') priority_rank = filters.RangeFilter(field_name='priority_rank') @@ -1009,7 +1044,15 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet): return Response(serializers.SchedulingUnitDraftExtendedSerializer(scheduling_unit_draft_copy, context={'request':request}).data, status=status.HTTP_201_CREATED) - + @swagger_auto_schema(responses={200: 'The system events of this scheduling unit blueprint', + 403: 'forbidden'}, + operation_description="Get the system events of this scheduling unit blueprint.") + @action(methods=['get'], detail=True, url_name="system_events") + def system_events(self, request, pk=None): + sub = get_object_or_404(models.SchedulingUnitBlueprint, pk=pk) + system_events = self.filter_queryset(sub.system_events) + serializer = self.get_serializer(system_events, many=True) + return RestResponse(serializer.data) class SchedulingUnitBlueprintExtendedViewSet(SchedulingUnitBlueprintViewSet): @@ -1176,8 +1219,18 @@ class TaskBlueprintPropertyFilter(property_filters.PropertyFilterSet): relative_start_time_max = property_filters.PropertyDurationFilter(field_name='relative_start_time', lookup_expr='lte') relative_stop_time_min = property_filters.PropertyDurationFilter(field_name='relative_stop_time', lookup_expr='gte') relative_stop_time_max = property_filters.PropertyDurationFilter(field_name='relative_stop_time', lookup_expr='lte') - start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='start_time') - stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='stop_time') + on_sky_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='on_sky_start_time') + on_sky_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='on_sky_stop_time') + process_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='process_start_time') + process_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='process_stop_time') + scheduled_on_sky_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_on_sky_start_time') + scheduled_on_sky_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_on_sky_stop_time') + scheduled_process_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_process_start_time') + scheduled_process_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='scheduled_process_stop_time') + actual_on_sky_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_on_sky_start_time') + actual_on_sky_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_on_sky_stop_time') + actual_process_start_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_process_start_time') + actual_process_stop_time = property_filters.PropertyIsoDateTimeFromToRangeFilter(field_name='actual_process_stop_time') status = property_filters.PropertyMultipleChoiceFilter(field_name='status', choices=tuple((i.value, i.value) for i in models.TaskBlueprint.Status), lookup_expr='iexact') subtasks = filters.ModelMultipleChoiceFilter(field_name='subtasks', queryset=models.Subtask.objects.all()) subtasks_min = filters.NumberFilter(field_name='subtasks__id', lookup_expr='gte') @@ -1311,6 +1364,17 @@ class TaskBlueprintViewSet(LOFARViewSet): return RestResponse(serializer.data) + @swagger_auto_schema(responses={200: 'The system events of this task blueprint', + 403: 'forbidden'}, + operation_description="Get the system events of this task blueprint.") + @action(methods=['get'], detail=True, url_name="system_events") + def system_events(self, request, pk=None): + tb = get_object_or_404(models.TaskBlueprint, pk=pk) + system_events = self.filter_queryset(tb.system_events) + serializer = self.get_serializer(system_events, many=True) + return RestResponse(serializer.data) + + class TaskBlueprintNestedViewSet(LOFARNestedViewSet): queryset = models.TaskBlueprint.objects.all() serializer_class = serializers.TaskBlueprintSerializer @@ -1384,3 +1448,64 @@ class PriorityQueueTypeViewSet(LOFARViewSet): class ProjectStateViewSet(LOFARViewSet): queryset = models.ProjectState.objects.all() serializer_class = serializers.ProjectStateSerializer + + +class SystemEventTypeViewSet(LOFARViewSet): + queryset = models.SystemEventType.objects.all() + serializer_class = serializers.SystemEventTypeSerializer + + +class SystemEventSubtypeViewSet(LOFARViewSet): + queryset = models.SystemEventSubtype.objects.all() + serializer_class = serializers.SystemEventSubtypeSerializer + + +class SystemEventSeverityViewSet(LOFARViewSet): + queryset = models.SystemEventSeverity.objects.all() + serializer_class = serializers.SystemEventSeveritySerializer + + +class SystemEventStatusViewSet(LOFARViewSet): + queryset = models.SystemEventStatus.objects.all() + serializer_class = serializers.SystemEventStatusSerializer + + +class SystemEventTemplateViewSet(AbstractTemplateViewSet): + queryset = models.SystemEventTemplate.objects.all() + serializer_class = serializers.SystemEventTemplateSerializer + + +class SystemEventFilter(filters.FilterSet): + name = filters.CharFilter(field_name='name', lookup_expr='icontains') + created_by = filters.CharFilter(field_name='created_by__username', lookup_expr='icontains') + id = NumberInFilter(field_name='id', lookup_expr='in') + id_min = filters.NumberFilter(field_name='id', lookup_expr='gte') + id_max = filters.NumberFilter(field_name='id', lookup_expr='lte') + created_at = filters.IsoDateTimeFromToRangeFilter(field_name='created_at') + updated_at = filters.IsoDateTimeFromToRangeFilter(field_name='updated_at') + description = filters.CharFilter(field_name='description', lookup_expr='icontains') + start = filters.IsoDateTimeFromToRangeFilter(field_name='start') + stop = filters.IsoDateTimeFromToRangeFilter(field_name='stop') + issue_type = filters.ModelMultipleChoiceFilter(field_name='issue_type', queryset=models.SystemEventType.objects.all()) + issue_subtype = filters.ModelMultipleChoiceFilter(field_name='issue_subtype', queryset=models.SystemEventSubtype.objects.all()) + severity = filters.ModelMultipleChoiceFilter(field_name='severity', queryset=models.SystemEventSeverity.objects.all()) + notes = filters.CharFilter(field_name='notes', lookup_expr='icontains') + status = filters.ModelMultipleChoiceFilter(field_name='status', queryset=models.SystemEventStatus.objects.all()) + affected_hardware_doc_stations = ListContainsAnyFilter(field_name='affected_hardware_doc__stations') + affected_hardware_template_name = filters.CharFilter(field_name='affected_hardware_template__name', lookup_expr='icontains') + affected_tasks = filters.ModelMultipleChoiceFilter(field_name='affected_tasks', queryset=models.TaskBlueprint.objects.all()) + jira_url = filters.CharFilter(field_name='jira_url', lookup_expr='icontains') + + class Meta: + model = models.SystemEvent + fields = '__all__' + filter_overrides = FILTER_OVERRIDES + + +class SystemEventViewSet(LOFARViewSet): + queryset = models.SystemEvent.objects.all() + serializer_class = serializers.SystemEventSerializer + filter_class = SystemEventFilter + + def perform_create(self, serializer): + serializer.save(created_by=self.request.user) diff --git a/SAS/TMSS/backend/src/tmss/urls.py b/SAS/TMSS/backend/src/tmss/urls.py index fcd6f55b39c3ee0c26090ae2a5465b4610176f18..790db62e4ade2fcf3018ccd22d489144a6e89fca 100644 --- a/SAS/TMSS/backend/src/tmss/urls.py +++ b/SAS/TMSS/backend/src/tmss/urls.py @@ -133,6 +133,10 @@ router.register(r'quantity', viewsets.QuantityViewSet) router.register(r'task_type', viewsets.TaskTypeViewSet) router.register(r'priority_queue_type', viewsets.PriorityQueueTypeViewSet) router.register(r'project_state', viewsets.ProjectStateViewSet) +router.register(r'system_event_status', viewsets.SystemEventStatusViewSet) +router.register(r'system_event_type', viewsets.SystemEventTypeViewSet) +router.register(r'system_event_subtype', viewsets.SystemEventSubtypeViewSet) +router.register(r'system_event_severity', viewsets.SystemEventSeverityViewSet) # templates router.register(r'common_schema_template', viewsets.CommonSchemaTemplateViewSet) @@ -146,6 +150,7 @@ router.register(r'task_relation_selection_template', viewsets.TaskRelationSelect router.register(r'reservation_template', viewsets.ReservationTemplateViewSet) router.register(r'task_connector_type', viewsets.TaskConnectorTypeViewSet) router.register(r'reservation_strategy_template', viewsets.ReservationStrategyTemplateViewSet) +router.register(r'system_event_template', viewsets.SystemEventTemplateViewSet) # instances router.register(r'cycle', viewsets.CycleViewSet) @@ -156,6 +161,7 @@ router.register(r'project_quota', viewsets.ProjectQuotaViewSet) router.register(r'project_quota_archive_location', viewsets.ProjectQuotaArchiveLocationViewSet) router.register(r'setting', viewsets.SettingViewSet) router.register(r'reservation', viewsets.ReservationViewSet) +router.register(r'system_event', viewsets.SystemEventViewSet) router.register(r'scheduling_set', viewsets.SchedulingSetViewSet) router.register(r'scheduling_unit_draft_extended', viewsets.SchedulingUnitDraftExtendedViewSet) diff --git a/SAS/TMSS/backend/test/t_adapter.py b/SAS/TMSS/backend/test/t_adapter.py index 0ed300bba2c8a7d87f4136881c6f9a6d4fb564a8..0bdb68bde7ee6645e2948acdd7316182b5d368a7 100755 --- a/SAS/TMSS/backend/test/t_adapter.py +++ b/SAS/TMSS/backend/test/t_adapter.py @@ -20,6 +20,7 @@ # $Id: $ import os +import time import unittest import requests @@ -190,7 +191,7 @@ class ObservationParsetAdapterTest(unittest.TestCase): "tabs": [ { "coherent": True, - "pointing": { "angle1": 1.0, "angle2": 2.0 } + "pointing": { "angle1": 1.0, "angle2": 2.0, "target": "target1" } }, { "coherent": False @@ -263,7 +264,7 @@ class SIPadapterTest(unittest.TestCase): specifications_doc['stations']['filter'] = "HBA_210_250" feedback_template = models.DataproductFeedbackTemplate.objects.get(name='feedback') # feedback_doc = get_default_json_object_for_schema(feedback_template.schema) # todo <- fix the default generator, for some reason it does not produce valid json here... - feedback_doc = {'percentage_written': 100, 'frequency': {'subbands': [156], 'central_frequencies': [33593750.0], 'channel_width': 6103.515625, 'channels_per_subband': 32}, 'time': {'start_time': '2013-02-16T17:00:00', 'duration': 5.02732992172, 'sample_width': 2.00278016}, 'antennas': {'set': 'HBA_DUAL', 'fields': [{'type': 'HBA', 'field': 'HBA0', 'station': 'CS001'}, {'type': 'HBA', 'field': 'HBA1', 'station': 'CS001'}]}, 'target': {'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000'}}, 'samples': {'polarisations': ['XX', 'XY', 'YX', 'YY'], 'type': 'float', 'bits': 32, 'writer': 'standard', 'writer_version': '2.2.0', 'complex': True}, '$schema': 'http://127.0.0.1:8001/api/schemas/dataproductfeedbacktemplate/feedback/1#'} + feedback_doc = {'percentage_written': 100, 'frequency': {'subbands': [156], 'central_frequencies': [33593750.0], 'channel_width': 6103.515625, 'channels_per_subband': 32}, 'time': {'start_time': '2013-02-16T17:00:00', 'duration': 5.02732992172, 'sample_width': 2.00278016}, 'antennas': {'set': 'HBA_DUAL', 'fields': [{'type': 'HBA', 'field': 'HBA0', 'station': 'CS001'}, {'type': 'HBA', 'field': 'HBA1', 'station': 'CS001'}]}, 'target': {'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000', "target": "target1"}}, 'samples': {'polarisations': ['XX', 'XY', 'YX', 'YY'], 'type': 'float', 'bits': 32, 'writer': 'standard', 'writer_version': '2.2.0', 'complex': True}, '$schema': 'http://127.0.0.1:8001/api/schemas/dataproductfeedbacktemplate/feedback/1#'} for dp in specifications_doc['stations']['digital_pointings']: dp['subbands'] = list(range(8)) # Create SubTask(output) @@ -309,7 +310,7 @@ class SIPadapterTest(unittest.TestCase): specifications_doc['stations']['filter'] = "HBA_210_250" feedback_template = models.DataproductFeedbackTemplate.objects.get(name='feedback') # feedback_doc = get_default_json_object_for_schema(feedback_template.schema) # todo <- fix the default generator, for some reason it does not produce valid json here... - feedback_doc = {'percentage_written': 100, 'frequency': {'subbands': [156], 'central_frequencies': [33593750.0], 'channel_width': 6103.515625, 'channels_per_subband': 32}, 'time': {'start_time': '2013-02-16T17:00:00', 'duration': 5.02732992172, 'sample_width': 2.00278016}, 'antennas': {'set': 'HBA_DUAL', 'fields': [{'type': 'HBA', 'field': 'HBA0', 'station': 'CS001'}, {'type': 'HBA', 'field': 'HBA1', 'station': 'CS001'}]}, 'target': {'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000'}}, 'samples': {'polarisations': ['XX', 'XY', 'YX', 'YY'], 'type': 'float', 'bits': 32, 'writer': 'standard', 'writer_version': '2.2.0', 'complex': True}, '$schema': 'http://127.0.0.1:8001/api/schemas/dataproductfeedbacktemplate/feedback/1#'} + feedback_doc = {'percentage_written': 100, 'frequency': {'subbands': [156], 'central_frequencies': [33593750.0], 'channel_width': 6103.515625, 'channels_per_subband': 32}, 'time': {'start_time': '2013-02-16T17:00:00', 'duration': 5.02732992172, 'sample_width': 2.00278016}, 'antennas': {'set': 'HBA_DUAL', 'fields': [{'type': 'HBA', 'field': 'HBA0', 'station': 'CS001'}, {'type': 'HBA', 'field': 'HBA1', 'station': 'CS001'}]}, 'target': {'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000', "target": "target1"}}, 'samples': {'polarisations': ['XX', 'XY', 'YX', 'YY'], 'type': 'float', 'bits': 32, 'writer': 'standard', 'writer_version': '2.2.0', 'complex': True}, '$schema': 'http://127.0.0.1:8001/api/schemas/dataproductfeedbacktemplate/feedback/1#'} for dp in specifications_doc['stations']['digital_pointings']: dp['subbands'] = list(range(8)) # Create SubTask(output) @@ -377,7 +378,7 @@ class SIPadapterTest(unittest.TestCase): specifications_doc['stations']['filter'] = "HBA_110_190" feedback_template = models.DataproductFeedbackTemplate.objects.get(name='feedback') # feedback_doc = get_default_json_object_for_schema(feedback_template.schema) # todo <- fix the default generator, for some reason it does not produce valid json here... - feedback_doc = {'percentage_written': 100, 'frequency': {'subbands': [152], 'central_frequencies': [33593750.0], 'channel_width': 3051.7578125, 'channels_per_subband': 64}, 'time': {'start_time': '2013-02-16T17:00:00', 'duration': 5.02732992172, 'sample_width': 2.00278016}, 'antennas': {'set': 'HBA_DUAL', 'fields': [{'type': 'HBA', 'field': 'HBA0', 'station': 'CS001'}, {'type': 'HBA', 'field': 'HBA1', 'station': 'CS001'}]}, 'target': {'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000'}, 'coherent': True}, 'samples': {'polarisations': ['XX', 'XY', 'YX', 'YY'], 'type': 'float', 'bits': 32, 'writer': 'standard', 'writer_version': '2.2.0', 'complex': True}, 'files': ['stokes/SAP0/CS003HBA1/L773569_SAP000_B005_S0_P000_bf.h5', 'stokes/SAP0/RS106HBA/L773569_SAP000_B046_S0_P000_bf.h5'], '$schema': 'http://127.0.0.1:8001/api/schemas/dataproductfeedbacktemplate/feedback/1#'} + feedback_doc = {'percentage_written': 100, 'frequency': {'subbands': [152], 'central_frequencies': [33593750.0], 'channel_width': 3051.7578125, 'channels_per_subband': 64}, 'time': {'start_time': '2013-02-16T17:00:00', 'duration': 5.02732992172, 'sample_width': 2.00278016}, 'antennas': {'set': 'HBA_DUAL', 'fields': [{'type': 'HBA', 'field': 'HBA0', 'station': 'CS001'}, {'type': 'HBA', 'field': 'HBA1', 'station': 'CS001'}]}, 'target': {'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000', "target": "target1"}, 'coherent': True}, 'samples': {'polarisations': ['XX', 'XY', 'YX', 'YY'], 'type': 'float', 'bits': 32, 'writer': 'standard', 'writer_version': '2.2.0', 'complex': True}, 'files': ['stokes/SAP0/CS003HBA1/L773569_SAP000_B005_S0_P000_bf.h5', 'stokes/SAP0/RS106HBA/L773569_SAP000_B046_S0_P000_bf.h5'], '$schema': 'http://127.0.0.1:8001/api/schemas/dataproductfeedbacktemplate/feedback/1#'} for dp in specifications_doc['stations']['digital_pointings']: dp['subbands'] = list(range(8)) # Create SubTask(output) @@ -650,18 +651,23 @@ class ProjectReportTest(unittest.TestCase): cls.test_data_creator = TMSSRESTTestDataCreator(BASE_URL, AUTH) response = requests.get(cls.test_data_creator.django_api_url + '/', auth=cls.test_data_creator.auth) - def _get_SUB_with_subtask_and_set_status(self, status=None): + def _get_SUB_with_subtask_and_set_status(self, subtask_status=None, subtask_type='ingest', task_template=None): """ - Help method to create SUB, TaskBlueprint, Subtask and (optionally) set the latter's status. + Help method to create SUB, TaskBlueprint with (optionally) template, Subtask with (optionally) type and set its status. """ sub = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data(draft=self.scheduling_unit_draft)) - tb = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=sub)) - # Create Subtask of type 'ingest' - subtask_template = models.SubtaskTemplate.objects.create(**SubtaskTemplate_test_data(subtask_type_value='ingest')) - subtask = models.Subtask.objects.create(**Subtask_test_data(subtask_template=subtask_template, task_blueprint=tb)) - - if status: - set_subtask_state_following_allowed_transitions(subtask, status) + subtask_template = models.SubtaskTemplate.objects.create(**SubtaskTemplate_test_data(subtask_type_value=subtask_type)) + if task_template: + task_template = models.TaskTemplate.objects.get(name=task_template) + tb = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=sub, specifications_template=task_template)) + specs = task_template.add_defaults_to_json_object_for_schema({}) + subtask = models.Subtask.objects.create(**Subtask_test_data(task_blueprint=tb, subtask_template=subtask_template, specifications_doc=specs)) + else: + tb = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=sub)) + subtask = models.Subtask.objects.create(**Subtask_test_data(task_blueprint=tb, subtask_template=subtask_template)) + + if subtask_status: + set_subtask_state_following_allowed_transitions(subtask, subtask_status) return sub, tb, subtask @@ -674,7 +680,7 @@ class ProjectReportTest(unittest.TestCase): succeeded_sub, _, succeeded_subtask = self._get_SUB_with_subtask_and_set_status('finished') failed_sub, _, failed_subtask = self._get_SUB_with_subtask_and_set_status('finished') cancelled_sub, _, cancelled_subtask = self._get_SUB_with_subtask_and_set_status('cancelled') - not_cancelled_sub, _, not_cancelled_subtask = self._get_SUB_with_subtask_and_set_status('defined') + not_cancelled_sub, _, not_cancelled_subtask = self._get_SUB_with_subtask_and_set_status('defined', 'pipeline', 'preprocessing pipeline') # Set workflow flags so we have a successful and a failed SUBs SchedulingUnitProcess.objects.create(su=succeeded_sub, results_accepted=True) SchedulingUnitProcess.objects.create(su=failed_sub, results_accepted=False) @@ -727,6 +733,9 @@ class ProjectReportTest(unittest.TestCase): self.assertEqual(result['LTA dataproducts']['size__sum'], 246) # Just to check if the placeholder was added self.assertIsNotNone(result['SAPs exposure']) # TODO: Implement test properly. + # Assert processing resources + self.assertEqual(result['processing_resources']['CPUtimeRAW'], None) # TODO: Change it properly. + self.assertAlmostEqual(result['processing_resources']['CPU_time_used'], 146.4, places=4) # one pipeline -> 146 = 2(cores_per_task) x 122(parallel_tasks) x 600(duration) / 1000 # Assert contains_overlapping_observations is False self.assertEqual(result['contains_overlapping_observations'], False) diff --git a/SAS/TMSS/backend/test/t_feedback.py b/SAS/TMSS/backend/test/t_feedback.py index 8988b780c7417a3a9793a5bac0fc2207ee27da6f..ea02f7f95e1b88b5a4d1028700384ef6ee00cbd9 100755 --- a/SAS/TMSS/backend/test/t_feedback.py +++ b/SAS/TMSS/backend/test/t_feedback.py @@ -364,6 +364,9 @@ feedback_version=03.01.00 "samples": { "type": "float", "bits": 32, + }, + "target": { + 'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000', 'target': 'target1'} } } @@ -409,7 +412,7 @@ feedback_version=03.01.00 "bits": 32, }, "target": { - 'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000'} + 'pointing': {'angle1': 0, 'angle2': 0, 'direction_type': 'J2000', 'target': 'target1'} }, } diff --git a/SAS/TMSS/backend/test/t_scheduling.py b/SAS/TMSS/backend/test/t_scheduling.py index 92da7f2e8d7ef6830cc83e6905c16381af9d2069..d377f3e2f4cc03bbfcb3dfbfd04ff7d2de9fccdf 100755 --- a/SAS/TMSS/backend/test/t_scheduling.py +++ b/SAS/TMSS/backend/test/t_scheduling.py @@ -734,7 +734,7 @@ class SAPTest(unittest.TestCase): spec['stations']['digital_pointings'][0]['name'] = task_blueprint_data['specifications_doc']['SAPs'][0]['name'] spec['stations']['digital_pointings'][0]['subbands'] = [0] cluster_url = client.get_path_as_json_object('/cluster/1')['url'] - pointing = {"angle1": 7.6, "angle2": 5.4, "direction_type": "J2000"} + pointing = {"angle1": 7.6, "angle2": 5.4, "direction_type": "J2000", "target": "target1"} spec['stations']['digital_pointings'][0]['pointing'] = pointing subtask_data = test_data_creator.Subtask(specifications_template_url=subtask_template['url'], diff --git a/SAS/TMSS/backend/test/t_subtasks.py b/SAS/TMSS/backend/test/t_subtasks.py index 04432b0f5c967e760fb7c23129e875a7b6dfc387..d130aea001dd733fb99e1e884cb815cdfbacf6d2 100755 --- a/SAS/TMSS/backend/test/t_subtasks.py +++ b/SAS/TMSS/backend/test/t_subtasks.py @@ -231,12 +231,14 @@ class SubTasksCreationFromTaskBluePrint(unittest.TestCase): def test_create_subtasks_from_task_blueprint_translates_SAP_names(self): task_blueprint = create_task_blueprint_object_for_testing('target observation') - task_blueprint.specifications_doc['SAPs'] = [{'name': 'target1', 'target': 'target1', 'subbands': [], + task_blueprint.specifications_doc['SAPs'] = [{'name': 'target1', 'subbands': [], 'digital_pointing': {'angle1': 0.1, 'angle2': 0.1, - 'direction_type': 'J2000'}}, - {'name': 'target2', 'target': 'target2', 'subbands': [], + 'direction_type': 'J2000', + 'target': 'target1'}}, + {'name': 'target2', 'subbands': [], 'digital_pointing': {'angle1': 0.2, 'angle2': 0.2, - 'direction_type': 'J2000'}}] + 'direction_type': 'J2000', + 'target': 'target2'}}] subtask = create_observation_control_subtask_from_task_blueprint(task_blueprint) i = 0 for sap in task_blueprint.specifications_doc['SAPs']: @@ -345,12 +347,14 @@ class SubTasksCreationFromTaskBluePrintCalibrator(unittest.TestCase): target_task_blueprint = create_task_blueprint_object_for_testing() create_scheduling_relation_task_blueprint_for_testing(cal_task_blueprint, target_task_blueprint, placement='parallel') - target_task_blueprint.specifications_doc['SAPs'] = [{'name': 'target1', 'target': '', 'subbands': list(range(0, 150)), + target_task_blueprint.specifications_doc['SAPs'] = [{'name': 'target1', 'subbands': list(range(0, 150)), 'digital_pointing': {'angle1': 0.1, 'angle2': 0.1, - 'direction_type': 'J2000'}}, - {'name': 'target2', 'target': '', 'subbands': list(range(150, 300)), + 'direction_type': 'J2000', + 'target': 'target1'}}, + {'name': 'target2', 'subbands': list(range(150, 300)), 'digital_pointing': {'angle1': 0.2, 'angle2': 0.2, - 'direction_type': 'J2000'}}] + 'direction_type': 'J2000', + 'target': 'target2'}}] target_task_blueprint.save() with self.assertRaises(SubtaskCreationException) as cm: @@ -547,24 +551,24 @@ class SettingTest(unittest.TestCase): class SubTaskCreationFromTaskBlueprintBeamformer(unittest.TestCase): - saps = [{"name": "target1", "target": "target1", "subbands": [349, 372], - "digital_pointing": {"angle1": 0.24, "angle2": 0.25, "direction_type": "J2000"}}, - {"name": "target2", "target": "target2", "subbands": [309, 302], - "digital_pointing": {"angle1": 0.42, "angle2": 0.52, "direction_type": "J2000"}} + saps = [{"name": "target1", "subbands": [349, 372], + "digital_pointing": {"angle1": 0.24, "angle2": 0.25, "direction_type": "J2000", "target": "target1"}}, + {"name": "target2", "subbands": [309, 302], + "digital_pointing": {"angle1": 0.42, "angle2": 0.52, "direction_type": "J2000", "target": "target2"}} ] beamformers = [{"name": "beamformer1", "coherent": {"settings": {"stokes": "I", "time_integration_factor": 8, "subbands_per_file": 244, "channels_per_subband": 8 }, "SAPs": [{"name": "target1", "tabs": [{"relative": True, - "pointing": {"direction_type": "J2000", "angle1": 0.1, "angle2": 0.1}}, - {"relative": False, "pointing": {"direction_type": "J2000", "angle1": 0.2, "angle2": 0.2}}], + "pointing": {"direction_type": "J2000", "angle1": 0.1, "angle2": 0.1, "target": "target1"}}, + {"relative": False, "pointing": {"direction_type": "J2000", "angle1": 0.2, "angle2": 0.2, "target": "target1"}}], "tab_rings": {"count": 8, "width": 0.02}, "subbands": {"method": "copy"}}, {"name": "target2", "tabs": [{"relative": True, - "pointing": {"direction_type": "J2000", "angle1": 0.1, "angle2": 0.1}}, - {"relative": False, "pointing": {"direction_type": "J2000", "angle1": 0.2, "angle2": 0.2}}], + "pointing": {"direction_type": "J2000", "angle1": 0.1, "angle2": 0.1, "target": "target2"}}, + {"relative": False, "pointing": {"direction_type": "J2000", "angle1": 0.2, "angle2": 0.2, "target": "target2"}}], "tab_rings": {"count": 7, "width": 0.03}, "subbands": {"list":[10,20,30], "method": "copy"} }]}, @@ -657,12 +661,14 @@ class SubTaskCreationFromTaskBlueprintBeamformer(unittest.TestCase): def test_generate_tab_ring_pointings_returns_correct_pointings(self): - pointing = {"angle1": 0.11, "angle2": 0.22, "direction_type": "J2000"} + pointing = {"angle1": 0.11, "angle2": 0.22, "direction_type": "J2000", "target": "target1"} tab_rings = {"width": 1, "count": 1} # assert center pointing is returned tab_pointings = _generate_tab_ring_pointings(pointing, tab_rings) - self.assertIn(pointing, tab_pointings) + expected_pointing = pointing.copy() + expected_pointing.update({'target': 'TAB 0 around target1'}) + self.assertIn(expected_pointing, tab_pointings) # assert correct number of pointings is returned self.assertEqual(len(tab_pointings), 1+6) # center + 1 ring @@ -673,15 +679,16 @@ class SubTaskCreationFromTaskBlueprintBeamformer(unittest.TestCase): # assert width is considered tab_rings.update({'width': 42}) tab_pointings = _generate_tab_ring_pointings(pointing, tab_rings) - pointing.update({'angle2': pointing['angle2']+tab_rings['width']}) + expected_pointing = pointing.copy() + pointing.update({'angle2': pointing['angle2']+tab_rings['width'], 'target': 'TAB 1 around target1'}) self.assertIn(pointing, tab_pointings) def test_add_pointings_adds_correctly(self): - pointing_a = {"angle1": 0.11, "angle2": 0.22, "direction_type": "J2000"} - pointing_b = {"angle1": 0.88, "angle2": 0.66, "direction_type": "J2000"} + pointing_a = {"angle1": 0.11, "angle2": 0.22, "direction_type": "J2000", "target": "target_a"} + pointing_b = {"angle1": 0.88, "angle2": 0.66, "direction_type": "J2000", "target": "target_b"} pointing_sum = _add_pointings(pointing_a, pointing_b) - self.assertEqual(pointing_sum, {"angle1": 0.99, "angle2": 0.88, "direction_type": "J2000"}) + self.assertEqual(pointing_sum, {"angle1": 0.99, "angle2": 0.88, "direction_type": "J2000", "target": "target_a_+_target_b"}) def test_filter_subbands_filters_correctly(self): subbands = [1,3,4,5,10,11,12,13,19,20] diff --git a/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py index ee4e0ceac36ff5acb8100c810e5d76dbfcb765f3..47978f895f9d8f514165e318c788e20d1e129921 100755 --- a/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py +++ b/SAS/TMSS/backend/test/t_tmssapp_specification_REST_API.py @@ -1960,8 +1960,8 @@ class SchedulingUnitBlueprintTestCase(unittest.TestCase): subtask_2 = models.Subtask.objects.create(**Subtask_test_data(scheduled_on_sky_start_time=datetime(2050, 1, 5, 10, 0, 0), scheduled_on_sky_stop_time=datetime(2050, 1, 5, 14, 0, 0), task_blueprint=models.TaskBlueprint.objects.create(**TaskBlueprint_test_data()))) # assert - response_1 = GET_and_assert_equal_expected_code(self, BASE_URL + '/scheduling_unit_blueprint/?start_time_after=2050-01-01T9:00:00&stop_time_before=2050-01-01T15:00:00', 200) - response_2 = GET_and_assert_equal_expected_code(self, BASE_URL + '/scheduling_unit_blueprint/?start_time_after=2050-01-01T9:00:00&stop_time_before=2050-01-05T15:00:00', 200) + response_1 = GET_and_assert_equal_expected_code(self, BASE_URL + '/scheduling_unit_blueprint/?scheduled_on_sky_start_time_after=2050-01-01T9:00:00&scheduled_on_sky_stop_time_before=2050-01-01T15:00:00', 200) + response_2 = GET_and_assert_equal_expected_code(self, BASE_URL + '/scheduling_unit_blueprint/?scheduled_on_sky_start_time_after=2050-01-01T9:00:00&scheduled_on_sky_stop_time_before=2050-01-05T15:00:00', 200) self.assertEqual(response_1['count'], 1) self.assertEqual(response_2['count'], 2) @@ -3472,6 +3472,51 @@ class SystemRolePermissionTestCase(unittest.TestCase): self.assertEqual(response.status_code, 403) self.assertEqual(count, len(models.Project.objects.all())) +class SystemEventTestCase(unittest.TestCase): + + def test_SystemEvent_list_apiformat(self): + r = requests.get(BASE_URL + '/system_event/?format=api', auth=AUTH) + self.assertEqual(r.status_code, 200) + self.assertTrue("System Event List" in r.content.decode('utf8')) + + def test_SystemEvent_POST_and_GET(self): + se_test_data = test_data_creator.SystemEvent("se_%s" % uuid.uuid4()) + + # POST and GET a new item and assert correctness + r_dict = POST_and_assert_expected_response(self, BASE_URL + '/system_event/', se_test_data, 201, se_test_data) + url = r_dict['url'] + GET_OK_and_assert_equal_expected_response(self, url, se_test_data) + + + def test_SystemEvent_filters_for_stations(self): + template_url = GET_and_assert_equal_expected_code(self, BASE_URL + '/system_event_template/?name=affectedhardware', 200)['results'][0]['url'] + + se_test_data_1 = test_data_creator.SystemEvent("se_%s" % uuid.uuid4(), affected_hardware_template=template_url, + affected_hardware_doc={'stations': ['DE601', 'DE602']}) + se_test_data_2 = test_data_creator.SystemEvent("se_%s" % uuid.uuid4(), affected_hardware_template=template_url, + affected_hardware_doc={'stations': ['DE601', 'DE603']}) + se_test_data_3 = test_data_creator.SystemEvent("se_%s" % uuid.uuid4(), affected_hardware_template=template_url, + affected_hardware_doc={'stations': ['DE604', 'DE609']}) + + POST_and_assert_expected_response(self, BASE_URL + '/system_event/', se_test_data_1, 201, se_test_data_1) + POST_and_assert_expected_response(self, BASE_URL + '/system_event/', se_test_data_2, 201, se_test_data_2) + POST_and_assert_expected_response(self, BASE_URL + '/system_event/', se_test_data_3, 201, se_test_data_3) + + # assert + response_1 = GET_and_assert_equal_expected_code(self, BASE_URL + '/system_event/?affected_hardware_doc_stations=%s' % 'DE601', 200) + response_2 = GET_and_assert_equal_expected_code(self, BASE_URL + '/system_event/?affected_hardware_doc_stations=%s' % 'DE603,DE604', 200) + response_3 = GET_and_assert_equal_expected_code(self, BASE_URL + '/system_event/?affected_hardware_doc_stations=%s' % 'CS999', 200) + + self.assertEqual(response_1['count'], 2) + self.assertIn(se_test_data_1['name'], str(response_1)) + self.assertIn(se_test_data_2['name'], str(response_1)) + self.assertNotIn(se_test_data_3['name'], str(response_1)) + self.assertEqual(response_2['count'], 2) + self.assertNotIn(se_test_data_1['name'], str(response_2)) + self.assertIn(se_test_data_2['name'], str(response_2)) + self.assertIn(se_test_data_3['name'], str(response_2)) + self.assertEqual(response_3['count'], 0) + if __name__ == "__main__": unittest.main() diff --git a/SAS/TMSS/backend/test/tmss_test_data_rest.py b/SAS/TMSS/backend/test/tmss_test_data_rest.py index 7cdbbe22373511c76859dbbfc3b8b36c0989e22f..71bf02695ce6f4256cc44ab2f4435fe76a743020 100644 --- a/SAS/TMSS/backend/test/tmss_test_data_rest.py +++ b/SAS/TMSS/backend/test/tmss_test_data_rest.py @@ -866,6 +866,44 @@ class TMSSRESTTestDataCreator(): 'DELETE': DELETE or [], 'POST': POST or []} + + def SystemEventTemplate(self, name="systemeventtemplate1", schema:dict=None) -> dict: + if schema is None: + schema = minimal_json_schema(properties={"foo": {"type": "string", "default": "bar"}}) + + return { "name": name, + "description": 'My description', + "schema": schema, + "tags": ["TMSS", "TESTING"]} + + @property + def cached_system_event_template_url(self): + try: + return self._system_event_template_url + except AttributeError: + self._system_event_template_url = self.post_data_and_get_url(self.SystemEventTemplate(), '/system_event_template/') + return self._system_event_template_url + + + def SystemEvent(self, name="My System Event", start=None, stop=None, issue_type="human", issue_subtype="dataloss", + severity="major", status="open", affected_hardware_doc=None, affected_hardware_template=None): + + if affected_hardware_doc is None: + affected_hardware_doc = {} + + if affected_hardware_template is None: + affected_hardware_template = self.cached_system_event_template_url + + return {"affected_hardware_doc": affected_hardware_doc, + "affected_hardware_template": affected_hardware_template, + "issue_subtype": self.django_api_url + '/system_event_subtype/%s' % issue_subtype, + "issue_type": self.django_api_url + '/system_event_type/%s' % issue_type, + "name": name, + "severity": self.django_api_url + '/system_event_severity/%s' % severity, + "start": start if start is not None else datetime.utcnow().isoformat(), + "stop": stop if stop is not None else datetime.utcnow().isoformat(), + "status": self.django_api_url + '/system_event_status/%s' % status} + def wipe_cache(self): cached_url_attributes = [attr for attr in self.__dict__.keys() if attr.startswith('_') and attr.endswith('_url')] for attr in cached_url_attributes: diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js index a9bd822854a2a0899fa51477c09df4873bc2bea2..e24af0a09d3c76465b94d5fef8a7aadb3a704204 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js @@ -1157,8 +1157,10 @@ function dateFilterFn(rows, id, filterValue) { function dateRangeFilterFn(rows, id, filterValue) { const filteredRows = _.filter(rows, function (row) { // If cell value is null or empty - if (!row.values[id]) { + if (filterValue.length>0 && !row.values[id]) { return false; + } else if (!row.values[id]) { + return true; } //Remove microsecond if value passed is UTC string in format "YYYY-MM-DDTHH:mm:ss.sssss" let rowValue = moment.utc(row.values[id].split('.')[0]); @@ -2107,7 +2109,7 @@ function Table(props) { ); // console.log('columns List', visibleColumns.map((d) => d.id)); const storedColOrder = UtilService.localStore({ type: 'get', key: tablename+'colOrder'}); - if (storedColOrder) { + if (!_.isEmpty(storedColOrder)) { setColumnOrder(storedColOrder) } else if (columnOrders && columnOrders.length) { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js index 67c3ead44cf98cddac8ebeaad7289f94d46fc49f..06733143f938be78f8ce1357145dc194b6a7fbaa 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js @@ -42,7 +42,10 @@ export class ProjectCreate extends Component { priority_rank: '', quota: [], // Mandatory Field in the back end, so an empty array is passed can_trigger: false, - auto_pin: false + auto_pin: false, + auto_ingest: false, + piggyback_allowed_tbb: false, + piggyback_allowed_aartfaac: false }, projectQuota: {}, // Resource Allocations validFields: {}, // For Validation @@ -408,7 +411,10 @@ export class ProjectCreate extends Component { priority_rank: 0, quota: [], archive_location: null, - archive_subdirectory:"" + archive_subdirectory:"", + auto_ingest: false, + piggyback_allowed_tbb: false, + piggyback_allowed_aartfaac: false }, projectQuota: projectQuota, validFields: {}, @@ -536,18 +542,18 @@ export class ProjectCreate extends Component { {this.state.errors.priority_rank ? this.state.errors.priority_rank : ""} </label> </div> - </div> - <div className="p-field p-grid"> + </div> + <div className="p-field p-grid"> <label htmlFor="ltaStorage" className="col-lg-2 col-md-2 col-sm-12">LTA Storage Location</label> - <div className="col-lg-3 col-md-3 col-sm-12" > - <Dropdown inputId="ltaStore" - optionValue="url" - tooltip="LTA Storage" tooltipOptions={this.tooltipOptions} - value={this.state.project.archive_location} - options={this.state.ltaStorage} - onChange={(e) => this.setProjectParams('archive_location', e.target.value)} - placeholder="Select LTA Storage" /> - </div> + <div className="col-lg-3 col-md-3 col-sm-12" > + <Dropdown inputId="ltaStore" + optionValue="url" + tooltip="LTA Storage" tooltipOptions={this.tooltipOptions} + value={this.state.project.archive_location} + options={this.state.ltaStorage} + onChange={(e) => this.setProjectParams('archive_location', e.target.value)} + placeholder="Select LTA Storage" /> + </div> <div className="col-lg-1 col-md-1 col-sm-12"></div> <label htmlFor="preventdeletionafteringest" className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion After Ingest</label> <div className="col-lg-3 col-md-3 col-sm-12" data-testid="preventdeletionafteringest"> @@ -556,8 +562,34 @@ export class ProjectCreate extends Component { tooltipOptions={this.tooltipOptions} checked={this.state.project.auto_pin} onChange={e => this.setProjectParams('auto_pin', e.target.checked)}></Checkbox> </div> + </div> + <div className="p-field p-grid"> + <label htmlFor="ingestautomatically" className="col-lg-2 col-md-2 col-sm-12">Ingest Automatically</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="ingestautomatically"> + <Checkbox inputId="ingestautomatically" role="ingestautomatically" + tooltip="Ingest Automatically" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.auto_ingest} onChange={e => this.setProjectParams('auto_ingest', e.target.checked)}></Checkbox> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="allowcommensaltbb" className="col-lg-2 col-md-2 col-sm-12">Allow commensal TBB</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="allowcommensaltbb"> + <Checkbox inputId="allowcommensaltbb" role="allowcommensaltbb" + tooltip="Allow commensal TBB" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.piggyback_allowed_tbb} onChange={e => this.setProjectParams('piggyback_allowed_tbb', e.target.checked)}></Checkbox> + </div> + </div> + <div className="p-field p-grid"> + <label htmlFor="allowcommensalaartfaac" className="col-lg-2 col-md-2 col-sm-12">Allow commensal AARTFAAC</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="allowcommensalaartfaac"> + <Checkbox inputId="allowcommensalaartfaac" role="allowcommensalaartfaac" + tooltip="Allow commensal AARTFAAC" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.piggyback_allowed_aartfaac} onChange={e => this.setProjectParams('piggyback_allowed_aartfaac', e.target.checked)}></Checkbox> </div> - {this.defaultResourcesEnabled && this.state.resourceList && + </div> + {this.defaultResourcesEnabled && this.state.resourceList && <div className="p-fluid"> <div className="p-field p-grid"> <div className="col-lg-2 col-md-2 col-sm-112"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js index 4b10311e76bb2f15b000566ff023d607a2af8884..ceee1602f77a3721ad71cf748ba8210e3b39737b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js @@ -612,6 +612,32 @@ export class ProjectEdit extends Component { checked={this.state.project.auto_pin} onChange={e => this.setProjectParams('auto_pin', e.target.checked)}></Checkbox> </div> </div> + <div className="p-field p-grid"> + <label htmlFor="ingestautomatically" className="col-lg-2 col-md-2 col-sm-12">Ingest Automatically</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="ingestautomatically"> + <Checkbox inputId="ingestautomatically" role="ingestautomatically" + tooltip="Ingest Automatically" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.auto_ingest} onChange={e => this.setProjectParams('auto_ingest', e.target.checked)}></Checkbox> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="allowcommensaltbb" className="col-lg-2 col-md-2 col-sm-12">Allow commensal TBB</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="allowcommensaltbb"> + <Checkbox inputId="allowcommensaltbb" role="allowcommensaltbb" + tooltip="Allow commensal TBB" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.piggyback_allowed_tbb} onChange={e => this.setProjectParams('piggyback_allowed_tbb', e.target.checked)}></Checkbox> + </div> + </div> + <div className="p-field p-grid"> + <label htmlFor="allowcommensalaartfaac" className="col-lg-2 col-md-2 col-sm-12">Allow commensal AARTFAAC</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="allowcommensalaartfaac"> + <Checkbox inputId="allowcommensalaartfaac" role="allowcommensalaartfaac" + tooltip="Allow commensal AARTFAAC" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.piggyback_allowed_aartfaac} onChange={e => this.setProjectParams('piggyback_allowed_aartfaac', e.target.checked)}></Checkbox> + </div> + </div> {this.state.resourceList && <div className="p-fluid"> <div className="p-field p-grid"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js index 275c586ae65c5e263bdd03714ae9d0df788f9b85..a8185adeeffc6f27bae50220aca1ffa6f6615f48 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js @@ -49,32 +49,36 @@ export class ProjectList extends Component { name: "Cycles", filter: "select" }, - LOFAR_Observing_Time: { + 'LOFAR Observing Time': { name: "Observing time (Hrs)", filter: "range" }, - LOFAR_Observing_Time_prio_A: { + 'LOFAR Observing Time prio A': { name: "Observing time prio A (Hrs)", filter: "range" }, - LOFAR_Observing_Time_prio_B: { + 'LOFAR Observing Time prio B': { name: "Observing time prio B (Hrs)", filter: "range" }, - CEP_Processing_Time: { + 'CEP Processing Time': { name: "Processing time (Hrs)", filter: "range" }, - LTA_Storage: { + 'LTA Storage': { name: "LTA storage (TB)", filter: "range" }, - Number_of_triggers: { + 'Number of triggers': { name: "Number of Triggers", filter: "range" } }], optionalcolumns: [{ + auto_ingest: { + name: "Ingest Automatically", + filter: "switch" + }, priority_rank: { name: "Project Priority", filter: "range" @@ -116,7 +120,7 @@ export class ProjectList extends Component { }], defaultSortColumn: [{ id: "Name / Project Code", desc: false }], columnOrder: ["Action", "Name / Project Code", "Status", "Category of Project", "Description", - "LTA Storage Location", "LTA Storage Path", "Project Priority", + "LTA Storage Location", "Ingest Automatically", "Project Priority", "Trigger Priority", "Category of Period", "Cycles", "Trigger Allowed", "Observing time (Hrs)", "Observing time prio A (Hrs)", "Observing time prio B (Hrs)", "Processing time (Hrs)", "LTA storage (TB)", "Number of Triggers", @@ -159,7 +163,7 @@ export class ProjectList extends Component { if (cycleId) { projects = await CycleService.getProjectsByCycle(cycleId); } else { - projects = await ProjectService.getProjectList(); + projects = await ProjectService.getProjectList(true); } const projectArchiveLocation = await ProjectService.getAllProjectQuotaArchiveLocation(); projects = await ProjectService.getUpdatedProjectQuota(projects); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js index bdbe7240fe2b5ac85e6bb22ea6f90d36b0da1318..4bdcef1c151f358cf0b6dbfc5edd1727607a892b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js @@ -324,6 +324,14 @@ export class ProjectView extends Component { <div className="p-grid"> <label className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion After Ingest</label> <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.auto_pin?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <label className="col-lg-2 col-md-2 col-sm-12">Ingest Automatically</label> + <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.auto_ingest?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + </div> + <div className="p-grid"> + <label className="col-lg-2 col-md-2 col-sm-12">Allow commensal TBB</label> + <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.piggyback_allowed_tbb?'fa fa-check-circle':'fa fa-times-circle'}></i></span> + <label className="col-lg-2 col-md-2 col-sm-12">Allow commensal AARTFAAC</label> + <span className="col-lg-4 col-md-4 col-sm-12"><i className={this.state.project.piggyback_allowed_aartfaac?'fa fa-check-circle':'fa fa-times-circle'}></i></span> </div> <div className="p-fluid"> <div className="p-field p-grid"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js index 9d081a10cc2562ee31f8a6ef4ce7089bada529c5..11256e2133f779db96941a9d0b682d6617baa41e 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js @@ -45,13 +45,23 @@ class SchedulingUnitList extends Component{ scheduling_set: {name: "Scheduling Set",}, observation_strategy_template_name: {name: "Template Name"}, observation_strategy_template_description: {name: "Template Description"}, - start_time:{ - name:"Start Time", + on_sky_start_time:{ + name:"Observation Start Time", filter:"flatpickrDateRange", format:UIConstants.CALENDAR_DATETIME_FORMAT }, - stop_time:{ - name:"End Time", + on_sky_stop_time:{ + name:"Observation End Time", + filter:"flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT + }, + process_start_time:{ + name:"Process Start Time", + filter:"flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT + }, + process_stop_time:{ + name:"Process End Time", filter:"flatpickrDateRange", format:UIConstants.CALENDAR_DATETIME_FORMAT }, @@ -95,8 +105,10 @@ class SchedulingUnitList extends Component{ "Template ID", "Template Name", "Template Description", - "Start Time", - "End Time", + "Observation Start Time", + "Observation End Time", + "Process Start Time", + "Process End Time", "Duration (HH:mm:ss)", "Prevent Autodeletion", "Stations (CS/RS/IS)", @@ -152,8 +164,10 @@ class SchedulingUnitList extends Component{ "Stations (CS/RS/IS)":"filter-input-50", "Tasks content (O/P/I)":"filter-input-50", "Number of SAPs in the target observation":"filter-input-50", - "Start Time":"filter-input-150", - "End Time":"filter-input-150", + "Observation Start Time":"filter-input-150", + "Observation End Time":"filter-input-150", + "Process Start Time":"filter-input-150", + "Process End Time":"filter-input-150", "Created_At":"filter-input-150", "Updated_At":"filter-input-150", "Template ID":"filter-input-75", @@ -369,8 +383,8 @@ class SchedulingUnitList extends Component{ * Get server side filter column details form API */ async getFilterColumns(type) { - const columnOrderToBeRemove = ['Status', 'Workflow Status', 'Start Time', 'End Time']; - const columnDefinitionToBeRemove = ['status', 'workflowStatus', 'start_time', 'stop_time']; + const columnOrderToBeRemove = ['Status', 'Workflow Status', 'Observation Start Time', 'Observation End Time', 'Process Start Time', 'Process End Time' ]; + const columnDefinitionToBeRemove = ['status', 'workflowStatus', 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time']; const suFilters = await ScheduleService.getSchedulingUnitFilterDefinition(type); this.columnMap = []; let tmpDefaulColumns = _.cloneDeep(this.state.defaultcolumns[0]); @@ -1142,21 +1156,37 @@ class SchedulingUnitList extends Component{ let filters = UtilService.localStore({ type: 'get', key: "scheduleunit_list_"+this.state.suType }); if(filters.length > 0 ) { for( const filter of filters) { - if (filter.id === 'Start Time') { + if (filter.id === 'Observation Start Time') { + const values = filter.value; + if (values[0]) { + this.filterQry += 'on_sky_start_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + if (values[1]) { + this.filterQry += 'on_sky_start_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + } else if (filter.id === 'Observation End Time') { + const values = filter.value; + if (values[0]) { + this.filterQry += 'on_sky_stop_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + if (values[1]) { + this.filterQry += 'on_sky_stop_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + } else if (filter.id === 'Process Start Time') { const values = filter.value; if (values[0]) { - this.filterQry += 'start_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_start_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } if (values[1]) { - this.filterQry += 'start_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_start_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } - } else if (filter.id === 'End Time') { + } else if (filter.id === 'Process End Time') { const values = filter.value; if (values[0]) { - this.filterQry += 'stop_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_stop_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } if (values[1]) { - this.filterQry += 'stop_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_stop_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } } else if ((filter.id === 'Scheduling Unit ID' || filter.id === 'Linked Draft ID') && filter.value != '') { let columnDetails = _.find(this.state.columnMap, {displayName:filter.id}); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js index 481f1ec388c9ff1c0552d197cdb9563d909eae7e..92760509bd87567bd3e3c6894ee7bb5bc74ef898 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -61,8 +61,10 @@ class ViewSchedulingUnit extends Component { "Control ID", "Name", "Description", - "Start Time", - "End Time", + "Observation Start Time", + "Observation End Time", + "Process Start Time", + "Process End Time", "Duration (HH:mm:ss)", "Relative Start Time (HH:mm:ss)", "Relative End Time (HH:mm:ss)", @@ -78,7 +80,10 @@ class ViewSchedulingUnit extends Component { "Updated at" ], defaultcolumns: [{ - status_logs: "Status Logs", + status_logs: { + name: "Status Logs", + filter: 'none' + }, status: { name: "Status", filter: "select" @@ -91,15 +96,25 @@ class ViewSchedulingUnit extends Component { subTaskID: 'Control ID', name: "Name", description: "Description", - start_time: { - name: "Start Time", - filter: "date", - format: UIConstants.CALENDAR_DATETIME_FORMAT + on_sky_start_time: { + name: "Observation Start Time", + filter:"flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT }, - stop_time: { - name: "End Time", - filter: "date", - format: UIConstants.CALENDAR_DATETIME_FORMAT + on_sky_stop_time: { + name: "Observation End Time", + filter:"flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT + }, + process_start_time: { + name: "Process Start Time", + filter:"flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT + }, + process_stop_time: { + name: "Process End Time", + filter:"flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT }, duration: { name: "Duration (HH:mm:ss)", @@ -140,6 +155,10 @@ class ViewSchedulingUnit extends Component { "Cancelled": "filter-input-50", "Duration (HH:mm:ss)": "filter-input-75", "Template ID": "filter-input-50", + "Observation Start Time":"filter-input-150", + "Observation End Time":"filter-input-150", + "Process Start Time":"filter-input-150", + "Process End Time":"filter-input-150", // "BluePrint / Task Draft link": "filter-input-100", "Relative Start Time (HH:mm:ss)": "filter-input-75", "Relative End Time (HH:mm:ss)": "filter-input-75", @@ -253,7 +272,7 @@ class ViewSchedulingUnit extends Component { this.setToggleBySorting(); let schedule_id = this.props.match.params.id; let schedule_type = this.props.match.params.type; - const permissionById = await AuthUtil.getUserPermissionByModuleId(schedule_type === 'blueprint'? 'scheduling_unit_blueprint': 'scheduling_unit_draft', schedule_id); + const permissionById = await AuthUtil.getUserPermissionByModuleId(schedule_type === 'blueprint'? 'scheduling_unit_blueprint': 'scheduling_unit_draft', schedule_id) this.setState({userPermission: permission, permissionById: permissionById, schedule_id: schedule_id}); if (schedule_type && schedule_id) { this.stations = await ScheduleService.getStationGroup(); @@ -376,8 +395,8 @@ class ViewSchedulingUnit extends Component { } async getFilterColumns(type) { - const columnOrderToRemove = type === 'blueprint' ?[] :['Status', 'Status Logs', 'Start Time', 'End Time', 'Control ID','#Dataproducts', 'Data Size', 'Data Size on Disk', 'Subtask Content']; - const columnDefinitionToRemove = type === 'blueprint' ?['scheduling_unit_draft']:['scheduling_unit_blueprint', 'status', 'status_logs', 'start_time', 'stop_time', 'subTaskID','noOfOutputProducts','size','dataSizeOnDisk','subtaskContent']; + const columnOrderToRemove = type === 'blueprint' ?[] :['Status', 'Status Logs', 'Observation Start Time', 'Observation End Time', 'Process Start Time', 'Process End Time', 'Control ID','#Dataproducts', 'Data Size', 'Data Size on Disk', 'Subtask Content']; + const columnDefinitionToRemove = type === 'blueprint' ?['scheduling_unit_draft']:['scheduling_unit_blueprint', 'status', 'status_logs', 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time', 'subTaskID','noOfOutputProducts','size','dataSizeOnDisk','subtaskContent']; let tmpDefaulColumns = _.cloneDeep(this.state.defaultcolumns[0]); let tmpOptionalColumns = _.cloneDeep(this.state.optionalcolumns[0]); tmpOptionalColumns.blueprint_draft = this.props.match.params.type==="draft"?"Linked Blueprint": "Linked Draft" @@ -479,7 +498,7 @@ class ViewSchedulingUnit extends Component { async getFormattedTaskDrafts(schedulingUnit) { let scheduletasklist = []; // Common keys for Task and Blueprint - let commonkeys = ['id', 'created_at', 'description', 'name', 'tags', 'updated_at', 'url', 'do_cancel', 'relative_start_time', 'relative_stop_time', 'start_time', 'stop_time', 'duration', 'status']; + let commonkeys = ['id', 'created_at', 'description', 'name', 'tags', 'updated_at', 'url', 'do_cancel', 'relative_start_time', 'relative_stop_time', 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time', 'duration', 'status']; for (const task of schedulingUnit.task_drafts) { let scheduletask = {}; scheduletask['tasktype'] = 'Draft'; @@ -1317,12 +1336,13 @@ class ViewSchedulingUnit extends Component { <label className="col-lg-2 col-md-2 col-sm-12">Updated At</label> <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.created_at && moment(this.state.scheduleunit.updated_at, moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span> </div> + {this.props.match.params.type === 'blueprint' && <div className="p-grid"> - <label className="col-lg-2 col-md-2 col-sm-12">Start Time</label> - <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.start_time && moment(this.state.scheduleunit.start_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span> - <label className="col-lg-2 col-md-2 col-sm-12">End Time</label> - <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.stop_time && moment(this.state.scheduleunit.stop_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span> - </div> + <label className="col-lg-2 col-md-2 col-sm-12">Observation Start Time</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.on_sky_start_time && moment(this.state.scheduleunit.on_sky_start_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span> + <label className="col-lg-2 col-md-2 col-sm-12">Observation End Time</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.scheduleunit.on_sky_stop_time && moment(this.state.scheduleunit.on_sky_stop_time).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span> + </div>} <div className="p-grid"> <label className="col-lg-2 col-md-2 col-sm-12" >Duration (HH:mm:ss)</label> <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc((this.state.scheduleunit.duration ? this.state.scheduleunit.duration : 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT)}</span> @@ -1426,7 +1446,6 @@ class ViewSchedulingUnit extends Component { </span> </div> </div> - {this.state.isLoading ? <AppLoader /> : (this.state.schedulingUnitTasks.length > 0) ? <ViewTable data={this.state.schedulingUnitTasks} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js index 8febeea0e6c417843b07a6758daccfa8ffa6e0dc..d703ec06dfec99efbe474f6f1357a9b2c7037183 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js @@ -82,12 +82,8 @@ export class FindObjectResult extends Component{ <span className="find-obj-tree-view"> <a href={subtaskDetails.url} target='_blank' title=" View SubTask API"><i className="fa fa-link" /></a></span></>; subtask['icon'] = 'fas fa-tasks'; - let tasks = []; - for (const taskId of subtaskDetails.task_blueprints_ids) { - let taskMap = await this.findTask('blueprint', taskId); - tasks.push(taskMap[0]); - } - subtask['children'] = tasks; + let task = await this.findTask('blueprint', subtaskDetails.task_blueprint_id); + subtask['children'] = task; return [subtask]; } return ''; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/dataproduct.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/dataproduct.js index 11ef48d543dcce7b4443e9eef226e42f8a50d810..497232b2dea73c646f1ff3aed50bfacff3f5897b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/dataproduct.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/dataproduct.js @@ -70,7 +70,8 @@ export class DataProduct extends Component{ await DataProductService.getSubtaskInputDataproduct(id).then(async inputdata =>{ for(const dataproduct of inputdata.data){ dataproduct['type'] = 'Input'; - dataproduct['size'] = UnitConverter.getUIResourceUnit('bytes', dataproduct['size']); + dataproduct['size'] = UnitConverter.getUIResourceUnit('gb', dataproduct['size']); + dataproduct['completed'] = dataproduct.feedback_doc.percentage_written; dataproduct['fullpath'] = dataproduct['directory']; dataproduct['storagelocation'] = storageLocation; dataproducts.push(dataproduct); @@ -79,7 +80,8 @@ export class DataProduct extends Component{ await DataProductService.getSubtaskOutputDataproduct(id).then(outputdata =>{ for(const dataproduct of outputdata.data){ dataproduct['type'] = 'Output'; - dataproduct['size'] = UnitConverter.getUIResourceUnit('bytes', dataproduct['size']); + dataproduct['size'] = UnitConverter.getUIResourceUnit('gb', dataproduct['size']); + dataproduct['completed'] = dataproduct.feedback_doc.percentage_written; dataproduct['fullpath'] = dataproduct['directory']; dataproduct['storagelocation'] = storageLocation; dataproducts.push(dataproduct); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js index 092c08eee35c1f03eb78fdffbd0ce6a8439f0521..1e3ddf44003f34118847dd2ac10637c27b6b0e34 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js @@ -53,8 +53,10 @@ export class TaskList extends Component { "Control ID", "Name", "Description", - "Start Time", - "End Time", + "Observation Start Time", + "Observation End Time", + "Process Start Time", + "Process End Time", "Duration (HH:mm:ss)", "Relative Start Time (HH:mm:ss)", "Relative End Time (HH:mm:ss)", @@ -106,13 +108,23 @@ export class TaskList extends Component { description: { name:"Description" }, - start_time: { - name: "Start Time", + on_sky_start_time: { + name: "Observation Start Time", filter: "flatpickrDateRange", format: UIConstants.CALENDAR_DATETIME_FORMAT }, - stop_time: { - name: "End Time", + on_sky_stop_time: { + name: "Observation End Time", + filter: "flatpickrDateRange", + format: UIConstants.CALENDAR_DATETIME_FORMAT + }, + process_start_time: { + name: "Process Start Time", + filter: "flatpickrDateRange", + format: UIConstants.CALENDAR_DATETIME_FORMAT + }, + process_stop_time: { + name: "Process End Time", filter: "flatpickrDateRange", format: UIConstants.CALENDAR_DATETIME_FORMAT }, @@ -182,8 +194,10 @@ export class TaskList extends Component { "Linked Draft ID": "filter-input-75", "Relative Start Time (HH:mm:ss)": "filter-input-75", "Relative End Time (HH:mm:ss)": "filter-input-75", - "Start Time": "filter-input-150", - "End Time": "filter-input-150", + "Observation Start Time": "filter-input-150", + "Observation End Time": "filter-input-150", + "Process Start Time": "filter-input-150", + "Process End Time": "filter-input-150", "Status": "filter-input-125", "#Dataproducts": "filter-input-75", "Data size": "filter-input-50", @@ -265,7 +279,7 @@ export class TaskList extends Component { getFormattedTaskDrafts(tasks) { let scheduletasklist = []; // Common keys for Task and Blueprint - let commonkeys = ['id', 'created_at', 'description', 'name', 'tags', 'updated_at', 'url', 'do_cancel', 'relative_start_time', 'relative_stop_time', 'start_time', 'stop_time', 'duration', 'status']; + let commonkeys = ['id', 'created_at', 'description', 'name', 'tags', 'updated_at', 'url', 'do_cancel', 'relative_start_time', 'relative_stop_time', 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time', 'duration', 'status']; for (const task of tasks) { let scheduletask = {}; scheduletask['tasktype'] = 'Draft'; @@ -615,8 +629,8 @@ export class TaskList extends Component { */ async getFilterColumns(type) { const taskFilters = await TaskService.getTaskFilterDefinition(type); - const columnOrderToRemove = this.state.taskType === 'Blueprint' ?[] :['Status', 'Status Logs', 'Start Time', 'End Time', 'Control ID','#Dataproducts', 'Data Size', 'Data Size on Disk', 'Subtask Content']; - const columnDefinitionToRemove = this.state.taskType === 'Blueprint' ?['scheduling_unit_draft']:['scheduling_unit_blueprint', 'status', 'status_logs', 'start_time', 'stop_time', 'subTaskID','noOfOutputProducts','size','dataSizeOnDisk','subtaskContent']; + const columnOrderToRemove = this.state.taskType === 'Blueprint' ?[] :['Status', 'Status Logs', 'Observation Start Time', 'Observation End Time', 'Process Start Time', 'Process End Time', 'Control ID','#Dataproducts', 'Data Size', 'Data Size on Disk', 'Subtask Content']; + const columnDefinitionToRemove = this.state.taskType === 'Blueprint' ?['scheduling_unit_draft']:['scheduling_unit_blueprint', 'status', 'status_logs', 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time','subTaskID','noOfOutputProducts','size','dataSizeOnDisk','subtaskContent']; this.columnMap = []; let tmpDefaulColumns = _.cloneDeep(this.state.defaultcolumns[0]); let tmpOptionalColumns = _.cloneDeep(this.state.optionalcolumns[0]); @@ -784,21 +798,37 @@ export class TaskList extends Component { let filters = UtilService.localStore({ type: 'get', key: "su_task_list_"+this.state.taskType }); if(filters.length > 0 ) { for( const filter of filters) { - if (filter.id === 'Start Time') { + if (filter.id === 'Observation Start Time') { + const values = filter.value; + if (values[0]) { + this.filterQry += 'on_sky_start_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + if (values[1]) { + this.filterQry += 'on_sky_start_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + } else if (filter.id === 'Observation End Time') { + const values = filter.value; + if (values[0]) { + this.filterQry += 'on_sky_stop_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + if (values[1]) { + this.filterQry += 'on_sky_stop_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + } + } if (filter.id === 'Process Start Time') { const values = filter.value; if (values[0]) { - this.filterQry += 'start_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_start_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } if (values[1]) { - this.filterQry += 'start_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_start_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } - } else if (filter.id === 'End Time') { + } else if (filter.id === 'Process End Time') { const values = filter.value; if (values[0]) { - this.filterQry += 'stop_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_stop_time_after='+ moment(new Date(values[0])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } if (values[1]) { - this.filterQry += 'stop_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; + this.filterQry += 'process_stop_time_before='+moment(new Date(values[1])).format("YYYY-MM-DDTHH:mm:ss")+".000Z&"; } } else if (filter.id === 'Scheduling Unit Name') { if (this.state.taskType === 'Draft') { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/summary.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/summary.js index 56b5ef83b03f165958e39229099cbba352e53b6f..270dca3aa1069184e1863b249dbbeef58f045e96 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/summary.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/summary.js @@ -58,7 +58,7 @@ export class TaskSummary extends Component { <label>Subtasks:</label> <ViewTable data={taskDetails.subtasks} - defaultcolumns={[{subtask_type: "Subtask Type", id: "Subtask ID", start_time:{name:"Start Time", format:UIConstants.CALENDAR_DATETIME_FORMAT}, stop_time:{name:"End Time", format:UIConstants.CALENDAR_DATETIME_FORMAT}, state_value: "Status"}]} + defaultcolumns={[{subtask_type: "Subtask Type", id: "Subtask ID", on_sky_start_time:{name:"Start Time", format:UIConstants.CALENDAR_DATETIME_FORMAT}, on_sky_stop_time:{name:"End Time", format:UIConstants.CALENDAR_DATETIME_FORMAT}, state_value: "Status"}]} optionalcolumns={[{actionpath: "actionpath"}]} columnclassname={[{"Subtask ID": "filter-input-50","Subtask Type":"filter-input-75", "Start Time": "filter-input-75", "End Time": "filter-input-75", "Status": "filter-input-75"}]} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js index 0190d3f5754533c374bc62055514ac8ec3e56957..47a04d5e6917b964d093ebda2efcfb996294f464 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js @@ -52,16 +52,26 @@ export class TaskView extends Component { filter:"select", tooltip: 'Select Subtask status' }, - start_time: { + on_sky_start_time: { name: "Start Time", filter: "flatpickrDateRange", format:UIConstants.CALENDAR_DATETIME_FORMAT, }, - stop_time: { + on_sky_stop_time: { name: "End Time", filter: "flatpickrDateRange", format:UIConstants.CALENDAR_DATETIME_FORMAT }, + process_start_time: { + name: "Process Start Time", + filter: "flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT, + }, + process_stop_time: { + name: "Process End Time", + filter: "flatpickrDateRange", + format:UIConstants.CALENDAR_DATETIME_FORMAT + }, duration:{ name:"Duration (HH:mm:ss)", filter: "durationMinMax", @@ -69,6 +79,7 @@ export class TaskView extends Component { }, parset: { name: "Link to Parset", + filter: 'none', tooltip: 'Enter few characters' } }], @@ -171,7 +182,7 @@ export class TaskView extends Component { TaskService.getTaskDetails(taskType, taskId, this.fetchSubtask) .then(async(task) => { if (task) { - taskType === 'blueprint' && this.getSubtaskDetails(task.subtasks); + taskType === 'blueprint' && await this.getSubtaskDetails(task.subtasks); const suId = taskType==='draft'?task.scheduling_unit_draft_id:task.scheduling_unit_blueprint_id; let loadTasks = false; const taskTemplate = await TaskService.getTaskTemplate(task.specifications_template_id); @@ -209,10 +220,12 @@ export class TaskView extends Component { subtaskRow['type'] = subtask.subtask_type; subtaskRow['id'] = subtask.id; subtaskRow['status'] = subtask.state_value; - subtaskRow['start_time']= subtask.start_time; - subtaskRow['stop_time']= subtask.stop_time; + subtaskRow['on_sky_start_time']= subtask.on_sky_start_time; + subtaskRow['on_sky_stop_time']= subtask.on_sky_stop_time; + subtaskRow['process_start_time']= subtask.process_start_time; + subtaskRow['process_stop_time']= subtask.process_stop_time; subtaskRow['duration']= moment.utc((subtask.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT); - subtaskRow['parset']= `https://proxy.lofar.eu/inspect/${subtask.id}/rtcp-${subtask.id}.parset`; + subtaskRow['parset']= `Parset`; subtaskRow['links'] = ['Link to Parset']; subtaskRow['linksURL'] = { 'Link to Parset': `https://proxy.lofar.eu/inspect/${subtask.id}/rtcp-${subtask.id}.parset` @@ -472,17 +485,28 @@ export class TaskView extends Component { <label className="col-lg-2 col-md-2 col-sm-12">Updated At</label> <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.task.updated_at).format(UIConstants.CALENDAR_DATETIME_FORMAT)}</span> </div> + {this.state.taskType === 'blueprint' && this.state.task.task_type === 'observation' && <div className="p-grid"> - <label className="col-lg-2 col-md-2 col-sm-12">Start Time</label> - <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.start_time?moment(this.state.task.start_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span> - <label className="col-lg-2 col-md-2 col-sm-12">End Time</label> - <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.end_time?moment(this.state.task.end_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span> - </div> + <label className="col-lg-2 col-md-2 col-sm-12">Observation Start Time</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.on_sky_start_time?moment(this.state.task.on_sky_start_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span> + <label className="col-lg-2 col-md-2 col-sm-12">Observation End Time</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.on_sky_stop_time?moment(this.state.task.on_sky_stop_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span> + </div>} + {this.state.taskType === 'blueprint' && + <div className="p-grid"> + <label className="col-lg-2 col-md-2 col-sm-12">Process Start Time</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.process_start_time?moment(this.state.task.process_start_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span> + <label className="col-lg-2 col-md-2 col-sm-12">Process End Time</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.process_stop_time?moment(this.state.task.process_stop_time,moment.ISO_8601).format(UIConstants.CALENDAR_DATETIME_FORMAT):""}</span> + </div>} <div className="p-grid"> {/* <label className="col-lg-2 col-md-2 col-sm-12">Tags</label> <Chips className="col-lg-4 col-md-4 col-sm-12 chips-readonly" disabled value={this.state.task.tags}></Chips> */} - <label className="col-lg-2 col-md-2 col-sm-12">Status</label> - <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.status}</span> + {this.state.taskType === 'blueprint' && + <> + <label className="col-lg-2 col-md-2 col-sm-12">Status</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.task.status}</span> + </>} {this.state.schedulingUnit && <> <label className="col-lg-2 col-md-2 col-sm-12">Scheduling Unit</label> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js index 2d86ba533db68ede29739b91719f2fffbfcf0d2e..05a68638e81ba9b5a61f4af4386421919cb279f1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js @@ -101,6 +101,7 @@ class TimelineListTabs extends Component { * @param {Array} filteredData - Array of SU table rows */ suListFilterCallback(filteredData) { + console.log("viewing SU list"); this.filteredSUB = filteredData; this.filteredTasks = null; this.props.suListFilterCallback(filteredData, this.filteredTasks, this.filteredReservs); @@ -112,6 +113,7 @@ class TimelineListTabs extends Component { * @param {Array} filteredData - Array of task table rows */ taskListFilterCallback(filteredData) { + console.log("viewing Task list"); this.filteredTasks = filteredData; this.props.suListFilterCallback(this.filteredSUB, filteredData, this.filteredReservs); } @@ -122,6 +124,7 @@ class TimelineListTabs extends Component { * @param {Array} filteredData - Array of reservation table rows */ reservListFilterCallback(filteredData) { + console.log("viewing Reserv list"); this.filteredReservs = filteredData; this.props.suListFilterCallback(this.filteredSUB, this.filteredTasks, filteredData); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js index 8e709b1c4bc94a5c394a8f25f8637585988c77a9..72822aff42d8294eaee630d059494ca43767a725 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js @@ -99,7 +99,8 @@ export class TimelineView extends Component { unschedulableBlueprint: {}, datasetStartTime: null, datasetEndTime:null, showDialog: false, - popPosition: {display: 'none'} + popPosition: {display: 'none'}, + isOnSkyView: this.timelineUIAttributes.isOnSkyView || false, // Flag to show on sky view in normal timeline view } this.STATUS_BEFORE_SCHEDULED = ['defining', 'defined', 'schedulable']; // Statuses before scheduled to get station_group this.allStationsGroup = []; @@ -258,6 +259,17 @@ export class TimelineView extends Component { this.loadSUsAheadAndTrail(null, datasetEndTime, null, endTime); } } + // Add dummy variables start_time and stop_time to minimize code changes and switch between different flavours + _.map(schedulingUnits.original, su => { + su.start_time = this.state.stationView || this.state.isOnSkyView?su.on_sky_start_time:(su.process_start_time || su.on_sky_start_time); + su.stop_time = this.state.stationView || this.state.isOnSkyView?su.on_sky_stop_time:(su.process_stop_time || su.on_sky_stop_time); + _.map(su.task_blueprints, taskBp => { + taskBp.start_time = this.state.stationView || this.state.isOnSkyView?taskBp.on_sky_start_time:(taskBp.process_start_time || taskBp.on_sky_start_time); + taskBp.stop_time = this.state.stationView || this.state.isOnSkyView?taskBp.on_sky_stop_time:(taskBp.process_stop_time || taskBp.on_sky_stop_time); + return taskBp; + }); + return su; + }); // Returning the data for the timeline only or the existing data. return schedulingUnits; } @@ -1004,7 +1016,7 @@ export class TimelineView extends Component { return stations; } - setStationView(e) { + async setStationView(e) { this.closeSummaryPanel(); let selectedGroups = this.state.selectedStationGroup; // Store selected view and station group. Remove for default values. Default is all station groups. @@ -1020,7 +1032,15 @@ export class TimelineView extends Component { delete this.timelineUIAttributes["stationView"]; } this.timelineCommonUtils.storeUIAttributes(this.timelineUIAttributes); - this.setState({ stationView: e.value, selectedStationGroup: selectedGroups }); + await this.setState({ stationView: e.value, selectedStationGroup: selectedGroups }); + this.dateRangeCallback(this.state.currentStartTime, this.state.currentEndTime, false); + } + + async setOnSkyView(value) { + await this.setState({isOnSkyView: value}); + this.timelineUIAttributes['isOnSkyView'] = value; + this.timelineCommonUtils.storeUIAttributes(this.timelineUIAttributes); + this.dateRangeCallback(this.state.currentStartTime, this.state.currentEndTime, false); } showOptionMenu(event) { @@ -1385,10 +1405,21 @@ export class TimelineView extends Component { } </div> <div className={`timeline-view-toolbar p-grid ${this.state.stationView && 'alignTimeLineHeader'}`}> - <div className="sub-header col-lg-2"> + <div className={`sub-header ${this.state.stationView?"col-lg-2":"col-lg-1"}`}> <label >Station View</label> <InputSwitch checked={this.state.stationView} onChange={(e) => { this.setStationView(e) }} /> </div> + {!this.state.stationView && + <div className="sub-header col-lg-1"> + {/* <label >On Sky View</label> + <InputSwitch checked={this.state.isOnSkyView} onChange={(e) => { this.setOnSkyView(e.value)}} /> */} + <Button className="p-button-rounded toggle-btn" + tooltip={this.state.isOnSkyView?"Turn off On-Sky View":"Turn on On-Sky View"} + style={{minWidth: "50px", backgroundColor: this.state.isOnSkyView?"#007AD9":"#cccccc", color: this.state.isOnSkyView?"#ffffff":"#7e8286"}} + label="On-Sky View" + onClick={e => this.setOnSkyView(!this.state.isOnSkyView)} /> + </div> + } {this.state.stationView && <> <div className="sub-header col-lg-2"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js index a8aca7e145674ffcb6bd3d5884c0ca4221700f08..a2ba1f2ed222b2acbd015e21d00f6b712b332473 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js @@ -24,6 +24,7 @@ import UIConstants from '../../utils/ui.constants'; import { TieredMenu } from 'primereact/tieredmenu'; import { InputSwitch } from 'primereact/inputswitch'; import { Dropdown } from 'primereact/dropdown'; +import { Button } from 'primereact/button'; import ReservationSummary from '../Reservation/reservation.summary'; import TimelineListTabs from './list.tabs'; import TimelineCommonUtils from './common.utils'; @@ -78,7 +79,8 @@ export class WeekTimelineView extends Component { datasetStartTime: null, datasetEndTime:null, showDialog: false, userrole: AuthStore.getState(), - popPosition: {display: 'none'} + popPosition: {display: 'none'}, + isOnSkyView: this.timelineUIAttributes.isOnSkyWeekView || false, // Flag to show on sky view in week view } this.STATUS_BEFORE_SCHEDULED = ['defining', 'defined', 'schedulable']; // Statuses before scheduled to get station_group this.reservations = []; @@ -204,6 +206,17 @@ export class WeekTimelineView extends Component { this.loadSUsAheadAndTrail(null, datasetEndTime, null, endTime); } } + // Add dummy variables start_time and stop_time to minimize code changes and switch between different flavours + _.map(schedulingUnits.original, su => { + su.start_time = this.state.stationView || this.state.isOnSkyView?su.on_sky_start_time:(su.process_start_time || su.on_sky_start_time); + su.stop_time = this.state.stationView || this.state.isOnSkyView?su.on_sky_stop_time:(su.process_stop_time || su.on_sky_stop_time); + _.map(su.task_blueprints, taskBp => { + taskBp.start_time = this.state.stationView || this.state.isOnSkyView?taskBp.on_sky_start_time:(taskBp.process_start_time || taskBp.on_sky_start_time); + taskBp.stop_time = this.state.stationView || this.state.isOnSkyView?taskBp.on_sky_stop_time:(taskBp.process_stop_time || taskBp.on_sky_stop_time); + return taskBp; + }); + return su; + }); // Returning the data for the timeline only or the existing data. return schedulingUnits; } @@ -375,7 +388,7 @@ export class WeekTimelineView extends Component { } else { const fetchDetails = !this.state.selectedItem || item.id !== this.state.selectedItem.id this.setState({ - selectedItem: item, isSUDetsVisible: true, + selectedItem: item, isSUDetsVisible: true, isReservDetsVisible: false, isSummaryLoading: fetchDetails, suTaskList: !fetchDetails ? this.state.suTaskList : [], canExtendSUList: false, canShrinkSUList: false @@ -825,6 +838,14 @@ export class WeekTimelineView extends Component { this.storeUIAttributes(); } + async setOnSkyView(value) { + await this.setState({isOnSkyView: value}); + this.timelineUIAttributes['isOnSkyWeekView'] = value; + let updatedItemGroupData = await this.dateRangeCallback(this.state.startTime, this.state.endTime, true); + this.timeline.updateTimeline(updatedItemGroupData); + this.storeUIAttributes(); + } + /** * Add Week Reservations during the visible timeline period * @param {Array} items @@ -1119,6 +1140,15 @@ export class WeekTimelineView extends Component { <InputSwitch checked={this.state.reservationEnabled} onChange={(e) => { this.showReservations(e) }} /> </div> + <div className="sub-header"> + {/* <label >On Sky View</label> + <InputSwitch checked={this.state.isOnSkyView} onChange={(e) => { this.setOnSkyView(e.value)}} /> */} + <Button className="p-button-rounded toggle-btn" + tooltip={this.state.isOnSkyView?"Turn off On-Sky View":"Turn on On-Sky View"} + style={{minWidth: "50px", backgroundColor: this.state.isOnSkyView?"#007AD9":"#cccccc", color: this.state.isOnSkyView?"#ffffff":"#7e8286"}} + label="On-Sky View" + onClick={e => this.setOnSkyView(!this.state.isOnSkyView)} /> + </div> {this.state.reservationEnabled && <div className="sub-header"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js index f0f2c47ae7e0233a9df5c234a3858b8b15ffd406..bc49f1bb86b8d2d0c23cb3e330ea81edbf2f78f9 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js @@ -254,9 +254,14 @@ const ProjectService = { return null; } }, - getProjectList: async function() { + /** + * Function returns project list with or without quota + * @param {Booleab} fetchQuota - flag to fetch quota or not + * @returns [] - array of project objects with/without quota populated. + */ + getProjectList: async function(fetchQuota) { try { - const response = await axios.get('/api/project/'); + const response = await axios.get(`/api/project/?${fetchQuota? 'expand=quota': ''}`); return response.data.results; } catch (error) { console.error('[project.services.getProjectList]',error); @@ -288,11 +293,9 @@ const ProjectService = { }) .then( async ()=>{ for(const project of projects){ - for(const id of project.quota_ids){ - await ProjectService.getProjectQuota(id).then(async quota =>{ + for(const quota of project.quota){ const resourceType = _.find(results.resourcetypes, ["name", quota.resource_type_id]); project[quota.resource_type_id] = resourceType?UnitConverter.getUIResourceUnit(resourceType.quantity_value, quota.value):quota.value; - }); } projects.map((pro,index) => { if(pro.name === project.name){ diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js index 86aac18f01bbd538adffca0ba491a8602d17928e..cfbb58bc0f5479a8c2e315f1fff2a96c435c60dd 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js @@ -17,6 +17,9 @@ const SU_FETCH_FIELDS = [ "id", "do_cancel", "start_time", "stop_time", + "process_start_time", "process_stop_time", + "on_sky_start_time", "on_sky_stop_time", + "scheduled_on_sky_start_time", "scheduled_on_sky_stop_time", "duration", "priority_rank", "updated_at", @@ -32,6 +35,9 @@ const SU_FETCH_FIELDS = [ "id", "task_blueprints.status", "task_blueprints.start_time", "task_blueprints.stop_time", + "task_blueprints.process_start_time", "task_blueprints.process_stop_time", + "task_blueprints.on_sky_start_time", "task_blueprints.on_sky_stop_time", + "task_blueprints.scheduled_on_sky_start_time", "task_blueprints.scheduled_on_sky_stop_time", "task_blueprints.task_type", "task_blueprints.do_cancel", "task_blueprints.duration", @@ -137,7 +143,7 @@ const ScheduleService = { let blueprints = []; try { let url = `/api/scheduling_unit_blueprint/?ordering=name&expand=${SU_EXPAND_FIELDS.join()}&fields=${SU_FETCH_FIELDS.join()}`; - url = `${url}&start_time_before=${endTime || ''}&stop_time_after=${startTime || ''}`; + url = `${url}&scheduled_on_sky_start_time_before=${endTime || ''}&scheduled_on_sky_stop_time_after=${startTime || ''}`; let initialResponse = await axios.get(url); const totalCount = initialResponse.data.count; const initialCount = initialResponse.data.results.length @@ -147,6 +153,11 @@ const ScheduleService = { let secondResponse = await axios.get(url); blueprints = blueprints.concat(secondResponse.data.results); } + _.map(blueprints, su => { + su.start_time = su.scheduled_on_sky_start_time; + su.stop_time = su.scheduled_on_sky_stop_time; + return su; + }); } catch(error) { console.error('[schedule.services.getExpandedSUList]',error); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js index 373a25edbd514f8e00125cfc47ee57119d342b06..affe6ba90d963ad88f6a65423d8559008e9dada0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js @@ -5,6 +5,7 @@ const UnitConverter = { resourceUnitMap: { 'time': { display: 'Hours', conversionFactor: 3600, mode: 'decimal', minFractionDigits: 0, maxFractionDigits: 2 }, 'bytes': { display: 'TB', conversionFactor: (1024 * 1024 * 1024 * 1024), mode: 'decimal', minFractionDigits: 0, maxFractionDigits: 3 }, + 'gb': { display: 'TB', conversionFactor: (1024), mode: 'decimal', minFractionDigits: 0, maxFractionDigits: 3 }, 'number': { display: 'Numbers', conversionFactor: 1, mode: 'decimal', minFractionDigits: 0, maxFractionDigits: 0 }, 'days': { display: 'Days', conversionFactor: (3600 * 24), mode: 'decimal', minFractionDigits: 0, maxFractionDigits: 0 } },