From ad49d367d7712a421d050ebb03842dee949f7413 Mon Sep 17 00:00:00 2001
From: Mattia Mancini <mancini@astron.nl>
Date: Mon, 10 Dec 2018 12:17:57 +0000
Subject: [PATCH] OSB-36: Wrong specification of the LBL antenna number

---
 .../monitoringdb/views/controllers.py         | 273 ++++++++++--------
 1 file changed, 159 insertions(+), 114 deletions(-)

diff --git a/LCU/Maintenance/DBInterface/monitoringdb/views/controllers.py b/LCU/Maintenance/DBInterface/monitoringdb/views/controllers.py
index ce5a8713348..239e049db52 100644
--- a/LCU/Maintenance/DBInterface/monitoringdb/views/controllers.py
+++ b/LCU/Maintenance/DBInterface/monitoringdb/views/controllers.py
@@ -2,25 +2,26 @@ import datetime
 import logging
 from collections import OrderedDict
 from math import ceil
-from django.conf import settings
+
 import coreapi
 import coreschema
+import pytz
 from django.db.models import Count
 from lofar.maintenance.monitoringdb.models.component_error import ComponentError
-from lofar.maintenance.monitoringdb.models.rtsm import RTSMErrorSummary, MODE_TO_COMPONENT, COMPONENT_TO_MODE
+from lofar.maintenance.monitoringdb.models.rtsm import RTSMErrorSummary, MODE_TO_COMPONENT, \
+    COMPONENT_TO_MODE
 from lofar.maintenance.monitoringdb.models.rtsm import RTSMObservation
 from lofar.maintenance.monitoringdb.models.station import Station
 from lofar.maintenance.monitoringdb.models.station_test import StationTest
 from rest_framework import status
-from rest_framework.reverse import reverse
 from rest_framework.response import Response
+from rest_framework.reverse import reverse
 from rest_framework.schemas import ManualSchema
 from rest_framework.views import APIView
-import pytz
-
 
 logger = logging.getLogger(__name__)
 
+
 def parse_date(date):
     expected_format = '%Y-%m-%d'
     try:
@@ -57,6 +58,65 @@ def _get_unique_error_types():
     return [item['type'] for item in ComponentError.objects.values('type').distinct()]
 
 
+def rcu_from_antenna_type_polarization(antenna_id, type, polarization):
+    """
+    Compute the rcu number for a given antenna number, type and polarization
+    :param antenna_id: id of the antenna
+    :param type: type of the antenna
+    :param polarization: polarization either [X, Y]
+    :return: the rcu id
+    :rtype: int
+    """
+    if polarization not in ['X', 'Y']:
+        raise ValueError('Polarization has to be either X or Y: %s not recognized' % polarization)
+
+    if type == 'LBH':
+        rcu_id = antenna_id * 2
+        rcu_id += 0 if polarization == 'X' else 1
+    elif type == 'LBL':
+        rcu_id = (antenna_id - 48) * 2
+        rcu_id += 1 if polarization == 'X' else 0
+    elif type == 'HBA':
+        rcu_id = antenna_id * 2
+        rcu_id += 0 if polarization == 'X' else 1
+    else:
+        rcu_id = -1
+    return rcu_id
+
+
+def antenna_id_polarization_from_rcu_type_polarization(rcu, type):
+    """
+    Compute the antenna id for a given rcu, type and polarization
+    :param rcu: id of the rcu
+    :param type: type of the antenna
+    :return: the antenna id and polarization
+    :rtype: (int, str)
+    :rtype: int
+    """
+    polarization_index = rcu % 2
+
+
+    if type in ['LBH', 'HBA']:
+        antenna_id = rcu - polarization_index
+        antenna_id /= 2.
+        polarization = 'Y' if polarization_index > 0 else 'X'
+    elif type == 'LBL':
+        antenna_id = (rcu - polarization_index)/2. + 48
+        polarization = 'X' if polarization_index > 0 else 'Y'
+    else:
+        antenna_id = -1
+        polarization = ''
+
+    return int(antenna_id), polarization
+
+def rcus_from_antenna_and_type(antenna_id, type):
+    rcu_x = rcu_from_antenna_type_polarization(antenna_id, type, 'X')
+    rcu_y = rcu_from_antenna_type_polarization(antenna_id, type, 'Y')
+
+    rcus = {rcu_x: 'X', rcu_y: 'Y'}
+    return rcus
+
+
 class ValidableReadOnlyView(APIView):
     """
     Convenience APIView class to have the validation of the query parameters on a get http request
@@ -65,6 +125,7 @@ class ValidableReadOnlyView(APIView):
     # Override this to make the schema validation work
     fields = []
     description = ''
+
     def compute_response(self):
         raise NotImplementedError()
 
@@ -197,7 +258,7 @@ class ControllerStationOverview(ValidableReadOnlyView):
                 station_test_payload = OrderedDict()
                 component_errors = station_test.component_errors
                 selected_component_errors = component_errors
-                if(self.error_types):
+                if (self.error_types):
                     selected_component_errors = component_errors.filter(type__in=self.error_types)
                 station_test_payload[
                     'total_component_errors'] = selected_component_errors.count()
@@ -235,8 +296,9 @@ class ControllerStationOverview(ValidableReadOnlyView):
                 rtsm_payload['mode'] = unique_modes
 
                 selected_rtsm_errors = rtsm.errors_summary
-                if(self.error_types):
-                    selected_rtsm_errors = rtsm.errors_summary.filter(error_type__in=self.error_types)
+                if (self.error_types):
+                    selected_rtsm_errors = rtsm.errors_summary.filter(
+                        error_type__in=self.error_types)
 
                 rtsm_payload['total_component_errors'] = selected_rtsm_errors.count()
 
@@ -322,9 +384,9 @@ class ControllerStationTestsSummary(ValidableReadOnlyView):
             station_test_payload = OrderedDict()
             station_test_payload['station_name'] = station_test.station.name
             selected_component_errors = station_test.component_errors
-            if(self.error_types):
-                selected_component_errors = selected_component_errors.filter(type__in=self.error_types)
-
+            if (self.error_types):
+                selected_component_errors = selected_component_errors.filter(
+                    type__in=self.error_types)
 
             station_test_payload[
                 'total_component_errors'] = selected_component_errors.count()
@@ -735,8 +797,8 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
             .filter(start_datetime__gte=self.from_date,
                     end_datetime__lte=self.to_date)
 
-        failing_component_types = station_tests.distinct('component_errors__component__type').\
-            exclude(component_errors__component__type__isnull=True).\
+        failing_component_types = station_tests.distinct('component_errors__component__type'). \
+            exclude(component_errors__component__type__isnull=True). \
             values_list('component_errors__component__type')
 
         for failing_component_type in failing_component_types:
@@ -752,7 +814,7 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
                 test_summary['end_date'] = station_test.end_datetime
                 component_errors_dict = OrderedDict()
                 test_summary['component_errors'] = component_errors_dict
-                component_errors = station_test.component_errors\
+                component_errors = station_test.component_errors \
                     .filter(component__type=failing_component_type)
                 if self.error_types:
                     component_errors = component_errors.filter(type__in=self.error_types)
@@ -764,7 +826,7 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
                     if component_id not in component_errors:
                         component_errors_dict[str(component_id)] = list()
                     component_errors_dict[str(component_id)] += [dict(error_type=error_type,
-                                                                     details=details)]
+                                                                      details=details)]
 
                 component_type_errors_list.append(test_summary)
 
@@ -782,6 +844,7 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
         for observing_mode in failing_component_modes:
 
             observing_mode = observing_mode[0]
+            component_name = MODE_TO_COMPONENT[observing_mode]
 
             rtsm_errors_per_component_type = list()
 
@@ -795,12 +858,12 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
                 component_errors_dict = OrderedDict()
                 rtsm_summary['component_errors'] = component_errors_dict
 
-                component_errors = rtsm_observation.errors_summary\
-                    .filter(mode=observing_mode)\
+                component_errors = rtsm_observation.errors_summary \
+                    .filter(mode=observing_mode) \
                     .values('error_type', 'start_frequency',
                             'stop_frequency', 'percentage',
                             'error_type', 'count', 'rcu', 'pk')
-                if component_errors.count() == 0 and rtsm_observation.errors_summary.count() !=0:
+                if component_errors.count() == 0 and rtsm_observation.errors_summary.count() != 0:
                     continue
                 else:
                     rtsm_errors_per_component_type.append(rtsm_summary)
@@ -810,20 +873,30 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
 
                 for component_error in component_errors:
 
-                    component_id = component_error['rcu']
-                    details = dict(percentage = component_error['percentage'],
-                                   start_frequency = component_error['start_frequency'],
-                                   stop_frequency = component_error['stop_frequency'],
-                                   count = component_error['count'])
+                    rcu_id = component_error['rcu']
+                    component_id, polarization = antenna_id_polarization_from_rcu_type_polarization(rcu_id, component_name)
+                    details = dict(percentage=component_error['percentage'],
+                                   start_frequency=component_error['start_frequency'],
+                                   stop_frequency=component_error['stop_frequency'],
+                                   count=component_error['count'],
+                                   component_id=component_id)
                     error_type = component_error['error_type']
                     # CHECKS IF THE ERROR IS PRESENT IN BOTH RCUS (hence, both polarizations of the antenna)
-                    url_to_plot = reverse('rtsm-summary-plot-detail', (component_error['pk'],), request=self.request)
+                    url_to_plot = reverse('rtsm-summary-plot-detail', (component_error['pk'],),
+                                          request=self.request)
                     details['url'] = url_to_plot
-                    if component_id not in component_errors:
-                        component_errors_dict[str(component_id)] = list()
-                    component_errors_dict[str(component_id)] += [dict(error_type=error_type, details=details)]
 
-            component_name = MODE_TO_COMPONENT[observing_mode]
+                    if not str(component_id) in component_errors_dict:
+                        component_errors_dict[str(component_id)] = OrderedDict()
+
+                    if error_type not in component_errors_dict[str(component_id)]:
+                        rtsm_error_summary = dict(error_type=error_type)
+                        component_errors_dict[str(component_id)][error_type] = rtsm_error_summary
+                    else:
+                        rtsm_error_summary = component_errors_dict[str(component_id)][error_type]
+
+                    rtsm_error_summary[polarization] = dict(details=details, error_type=error_type, rcu_id=rcu_id, polarization=polarization,)
+
             if component_name not in response_payload:
                 response_payload[component_name] = rtsm_errors_per_component_type
             else:
@@ -850,18 +923,19 @@ class ControllerStationComponentErrors(ValidableReadOnlyView):
             station_test_errors_per_type = station_test_errors.get(component_type, [])
             rtsm_errors_per_type = rtsm_errors.get(component_type, [])
 
-            payload[component_type] = sorted(station_test_errors_per_type + rtsm_errors_per_type, key=lambda item: item['start_date'], reverse=True)
+            payload[component_type] = sorted(station_test_errors_per_type + rtsm_errors_per_type,
+                                             key=lambda item: item['start_date'], reverse=True)
 
         return Response(status=status.HTTP_200_OK, data=payload)
 
 
 class ControllerStationComponentElementErrors(ValidableReadOnlyView):
-    station_name= None  # required
-    from_date= None  # required
-    to_date= None  # required
-    component_type= None  # required
-    antenna_id= None  # required
-    test_type= "A"
+    station_name = None  # required
+    from_date = None  # required
+    to_date = None  # required
+    component_type = None  # required
+    antenna_id = None  # required
+    test_type = "A"
 
     fields = [
         coreapi.Field(
@@ -910,69 +984,37 @@ class ControllerStationComponentElementErrors(ValidableReadOnlyView):
         )
     ]
 
-    def rcu_from_antenna_type_polarization(self, antenna_id, type, polarization):
-        """
-        Compute the rcu number for a given antenna number, type and polarization
-        :param antenna_id: id of the antenna
-        :param type: type of the antenna
-        :param polarization: polarization either [X, Y]
-        :return: the rcu id
-        :rtype: int
-        """
-        if polarization not in ['X', 'Y']:
-            raise ValueError('Polarization has to be either X or Y: %s not recognized' % polarization)
-
-        if type == 'LBH':
-            rcu_id = antenna_id * 2
-            rcu_id += 0 if polarization == 'X' else 1
-        elif type == 'LBL':
-            rcu_id = (antenna_id - 48) * 2
-            rcu_id += 1 if polarization == 'X' else 0
-        elif type == 'HBA':
-            rcu_id = antenna_id * 2
-            rcu_id += 0 if polarization == 'X' else 1
-        else:
-            rcu_id = -1
-        return rcu_id
-
-    def rcus_from_antenna_and_type(self, antenna_id, type):
-        rcu_x = self.rcu_from_antenna_type_polarization(antenna_id, type, 'X')
-        rcu_y = self.rcu_from_antenna_type_polarization(antenna_id, type, 'Y')
-
-        rcus={rcu_x:'X', rcu_y:'Y'}
-        return rcus
-
     def compute_ok_rtsm_list(self):
-        mode=COMPONENT_TO_MODE[self.component_type]
+        mode = COMPONENT_TO_MODE[self.component_type]
+
+        rcus_per_polarization = rcus_from_antenna_and_type(self.antenna_id,
+                                                           self.component_type)
 
-        rcus_per_polarization = self.rcus_from_antenna_and_type(self.antenna_id,
-                                                                self.component_type)
-        
         good_observation_list = RTSMObservation.objects.filter(errors_summary__mode__in=mode,
-                                       start_datetime__gt=self.from_date,
-                                       end_datetime__lt=self.to_date,
-                                       station__name=self.station_name).\
-            exclude(errors_summary__rcu__in=list(rcus_per_polarization.keys())).\
+                                                               start_datetime__gt=self.from_date,
+                                                               end_datetime__lt=self.to_date,
+                                                               station__name=self.station_name). \
+            exclude(errors_summary__rcu__in=list(rcus_per_polarization.keys())). \
             values('pk',
-                    'observation_id',
-                    'start_datetime',
-                    'end_datetime').distinct('pk')
+                   'observation_id',
+                   'start_datetime',
+                   'end_datetime').distinct('pk')
         result = []
         for observation in good_observation_list:
             entry = dict(test_id=observation['observation_id'],
-                 db_id=observation['pk'],
-                 start_date=observation['start_datetime'],
-                 end_date=observation['end_datetime'],
-                 test_type='R',
-                 component_errors=dict())
+                         db_id=observation['pk'],
+                         start_date=observation['start_datetime'],
+                         end_date=observation['end_datetime'],
+                         test_type='R',
+                         component_errors=dict())
             result.append(entry)
         return result
 
     def compute_ok_station_test(self):
         good_station_test = StationTest.objects.filter(start_datetime__gt=self.from_date,
-                                                           end_datetime__lt=self.to_date,
-                                                           station__name=self.station_name).\
-            exclude(component_errors__component__component_id=self.antenna_id).\
+                                                       end_datetime__lt=self.to_date,
+                                                       station__name=self.station_name). \
+            exclude(component_errors__component__component_id=self.antenna_id). \
             values('pk', 'start_datetime', 'end_datetime').order_by('-start_datetime')
         result = []
         for station_test in good_station_test:
@@ -987,21 +1029,21 @@ class ControllerStationComponentElementErrors(ValidableReadOnlyView):
 
     def compute_rtsm_errors_list(self):
         errors = dict()
-        rcus_per_polarization = self.rcus_from_antenna_and_type(self.antenna_id,
-                                                                self.component_type)
+        rcus_per_polarization = rcus_from_antenna_and_type(self.antenna_id,
+                                                           self.component_type)
 
         rtsm_errors = RTSMErrorSummary.objects.values('observation__pk',
-                                        'observation__start_datetime',
-                                        'observation__end_datetime',
-                                        'observation__observation_id',
-                                        'observation__station__name',
-                                        'pk',
-                                        'rcu',
-                                        'mode',
-                                        'error_type',
-                                        'percentage',
-                                        'count',
-                                        'observation__samples').filter(
+                                                      'observation__start_datetime',
+                                                      'observation__end_datetime',
+                                                      'observation__observation_id',
+                                                      'observation__station__name',
+                                                      'pk',
+                                                      'rcu',
+                                                      'mode',
+                                                      'error_type',
+                                                      'percentage',
+                                                      'count',
+                                                      'observation__samples').filter(
             observation__start_datetime__gt=self.from_date,
             observation__end_datetime__lt=self.to_date,
             observation__station__name=self.station_name,
@@ -1011,13 +1053,13 @@ class ControllerStationComponentElementErrors(ValidableReadOnlyView):
             observation_pk = item['observation__pk']
             if observation_pk not in errors.keys():
                 errors[observation_pk] = dict(test_id=item['observation__observation_id'],
-                                                       db_id=item['observation__pk'],
-                                                       start_date=item[
-                                                           'observation__start_datetime'],
-                                                       end_date=item[
-                                                           'observation__end_datetime'],
-                                                       test_type='R',
-                                                       component_errors=dict())
+                                              db_id=item['observation__pk'],
+                                              start_date=item[
+                                                  'observation__start_datetime'],
+                                              end_date=item[
+                                                  'observation__end_datetime'],
+                                              test_type='R',
+                                              component_errors=dict())
             rcu = item['rcu']
             polarization = rcus_per_polarization[rcu]
 
@@ -1031,12 +1073,13 @@ class ControllerStationComponentElementErrors(ValidableReadOnlyView):
                 errors[observation_pk]['component_errors'][error_type] = dict()
 
             url_to_plot = reverse('rtsm-summary-plot-detail', (item['pk'],), request=self.request)
-            errors[observation_pk]['component_errors'][error_type][polarization] = dict(samples=samples,
-                                                                              percentage=percentage,
-                                                                              count=count,
-                                                                              mode=mode,
-                                                                              rcu=rcu,
-                                                                              url=url_to_plot)
+            errors[observation_pk]['component_errors'][error_type][polarization] = dict(
+                samples=samples,
+                percentage=percentage,
+                count=count,
+                mode=mode,
+                rcu=rcu,
+                url=url_to_plot)
         return list(errors.values())
 
     def compute_station_tests_error_list(self):
@@ -1058,11 +1101,13 @@ class ControllerStationComponentElementErrors(ValidableReadOnlyView):
                                                component_errors=dict())
             error_type = component_error.type
             details = component_error.details
-            errors[station_test_pk]['component_errors'][error_type] = dict(details=details, element_errors = dict())
+            errors[station_test_pk]['component_errors'][error_type] = dict(details=details,
+                                                                           element_errors=dict())
 
             error_per_error_type = errors[station_test_pk]['component_errors'][error_type]
 
-            for element in component_error.failing_elements.values('element__element_id', 'details'):
+            for element in component_error.failing_elements.values('element__element_id',
+                                                                   'details'):
                 element_id = element['element__element_id']
                 error_per_error_type["element_errors"][element_id] = element['details']
 
-- 
GitLab