diff --git a/monitoring/mock_uss/ridsp/routes_injection.py b/monitoring/mock_uss/ridsp/routes_injection.py index 6fb6065c83..9d7b2448f3 100644 --- a/monitoring/mock_uss/ridsp/routes_injection.py +++ b/monitoring/mock_uss/ridsp/routes_injection.py @@ -114,6 +114,7 @@ def ridsp_create_test(test_id: str) -> Tuple[str, int]: with db as tx: tx.tests[test_id] = record + tx.notifications.create_notifications_if_needed(record) return flask.jsonify( ChangeTestResponse(version=record.version, injected_flights=record.flights) ) diff --git a/monitoring/mock_uss/ridsp/user_notifications.py b/monitoring/mock_uss/ridsp/user_notifications.py index 38f8bc3c3a..d42d810a69 100644 --- a/monitoring/mock_uss/ridsp/user_notifications.py +++ b/monitoring/mock_uss/ridsp/user_notifications.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, List import datetime import arrow @@ -9,6 +9,9 @@ UserNotification, Time, ) +from monitoring.monitorlib.rid_automated_testing.injection_api import TestFlight + +from . import database class ServiceProviderUserNotifications(ImplicitDict): @@ -29,3 +32,32 @@ def record_notification( self.user_notifications.append( UserNotification(observed_at=observed_at_time, message=message) ) + + def create_notifications_if_needed(self, record: "database.TestRecord"): + + for notif in check_and_generate_missing_fields_notifications(record.flights): + self.record_notification(notif) + + +def check_and_generate_missing_fields_notifications( + injected_flights: List[TestFlight], +) -> List[str]: + + missing_fields_notifications = [] + + for flight in injected_flights: + for tpos, telemetry in enumerate(flight.raw_telemetry): + for mandatory_field in flight.MANDATORY_TELEMETRY_FIELDS: + if telemetry.get(mandatory_field, None) is None: + missing_fields_notifications.append( + f"Flight #{flight.injection_id}, Telemetry #{tpos}, missing field {mandatory_field}" + ) + + if telemetry.get("position", None): + for mandatory_field in flight.MANDATORY_POSITION_FIELDS: + if telemetry["position"].get(mandatory_field, None) is None: + missing_fields_notifications.append( + f"Flight #{flight.injection_id}, Telemetry #{tpos}, missing field position.{mandatory_field}" + ) + + return missing_fields_notifications diff --git a/monitoring/monitorlib/rid_automated_testing/injection_api.py b/monitoring/monitorlib/rid_automated_testing/injection_api.py index d09d51328d..01eb4c7034 100644 --- a/monitoring/monitorlib/rid_automated_testing/injection_api.py +++ b/monitoring/monitorlib/rid_automated_testing/injection_api.py @@ -18,6 +18,65 @@ class TestFlight(injection.TestFlight): + + MANDATORY_TELEMETRY_FIELDS = [ + "timestamp", + "timestamp_accuracy", + "position", + "track", + "speed", + "speed_accuracy", + "vertical_speed", + ] + + # TODO: Handle accuracy_h and accuracy_v + MANDATORY_POSITION_FIELDS = ["lat", "lng", "alt"] + + raw_telemetry: Optional[List[RIDAircraftState]] + """Copy of original telemetry with potential invalid data""" + + def __init__(self, *args, **kwargs): + """Build a new test flight instance + + Args: + filter_invalid_telemetry: If enabled, the constructor will filter out any invalid telemetry data. A copy of initial data is kept in the raw_telemetry field. Default to true. + Any other argument is passed to the parent injection.TestFlight class. + """ + + super().__init__(*args, **kwargs) + + # We filter out bad telemetry but keep a copy in raw_telemetry + self.raw_telemetry = self.telemetry + + filter_invalid_telemetry = kwargs.pop("filter_invalid_telemetry", True) + + if filter_invalid_telemetry: + filtered_telemetry = [] + + for telemetry in self.telemetry: + + is_ok = True + + for mandatory_field in self.MANDATORY_TELEMETRY_FIELDS: + if telemetry.get(mandatory_field, None) is None: + is_ok = False + break + + if not is_ok: + continue + + for mandatory_field in self.MANDATORY_POSITION_FIELDS: + if telemetry.position.get(mandatory_field, None) is None: + is_ok = False + break + + if not is_ok: + continue + + filtered_telemetry.append(telemetry) + + self.telemetry = filtered_telemetry + def get_span( self, ) -> Tuple[Optional[datetime.datetime], Optional[datetime.datetime]]: