def test_generates_choices(self):
     """Tests generates choices for a sample enum."""
     self.assertEqual([(1, 'N'), (2, 'NE'), (3, 'E'), (4, 'SE'), (5, 'S'),
                       (6, 'SW'), (7, 'W'), (8, 'NW')],
                      sorted(
                          pb_utils.FieldChoicesFromEnum(
                              interop_api_pb2.Odlc.Orientation)))
Example #2
0
class Map(models.Model):
    """Map submission for a team."""

    # The mission this is a map for.
    mission = models.ForeignKey('MissionConfig', on_delete=models.CASCADE)
    # The user which submitted and owns this map.
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             db_index=True,
                             on_delete=models.CASCADE)

    # Uploaded map.
    uploaded_map = models.ImageField(upload_to='maps', blank=True)

    # Quality assigned by a judge.
    quality = models.IntegerField(choices=pb_utils.FieldChoicesFromEnum(
        interop_admin_api_pb2.MapEvaluation.MapQuality),
                                  null=True,
                                  blank=True)
class MissionJudgeFeedback(models.Model):
    """Stores feedback from judges on a team's mission performance."""

    # The mission for which this is feedback.
    mission = models.ForeignKey(MissionConfig, on_delete=models.CASCADE)
    # The user for which this is feedback.
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)

    # Time spent occupying runway and airspace.
    flight_time = models.DurationField()
    # Time spent handling data on mission clock.
    post_process_time = models.DurationField()
    # Whether the team used their single timeout.
    used_timeout = models.BooleanField()

    # Whether the team had the min auto flight time.
    min_auto_flight_time = models.BooleanField()
    # The number of times the pilot took over.
    safety_pilot_takeovers = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Number of times the UAS went out of bounds.
    out_of_bounds = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Number of times out of bounds compromised safety.
    unsafe_out_of_bounds = models.IntegerField(validators=[
        validators.MinValueValidator(0),
    ])
    # Whether something fell off UAS during flight.
    things_fell_off_uas = models.BooleanField()
    # Whether the UAS crashed.
    crashed = models.BooleanField()

    # Accuracy of drop in feet.
    air_drop_accuracy = models.IntegerField(
        choices=pb_utils.FieldChoicesFromEnum(
            interop_admin_api_pb2.MissionJudgeFeedback.AirDropAccuracy))
    # Whether the UGV drove to the specified location.
    ugv_drove_to_location = models.BooleanField()

    # Grade of team performance [0, 100].
    operational_excellence_percent = models.FloatField(validators=[
        validators.MinValueValidator(0),
        validators.MaxValueValidator(100),
    ])

    class Meta:
        unique_together = (('mission', 'user'), )

    def proto(self):
        """Get the proto formatted feedback."""
        feedback = interop_admin_api_pb2.MissionJudgeFeedback()

        feedback.flight_time_sec = self.flight_time.total_seconds()
        feedback.post_process_time_sec = self.post_process_time.total_seconds()
        feedback.used_timeout = self.used_timeout

        feedback.min_auto_flight_time = self.min_auto_flight_time
        feedback.safety_pilot_takeovers = self.safety_pilot_takeovers
        feedback.out_of_bounds = self.out_of_bounds
        feedback.unsafe_out_of_bounds = self.unsafe_out_of_bounds
        feedback.things_fell_off_uas = self.things_fell_off_uas
        feedback.crashed = self.crashed

        feedback.air_drop_accuracy = self.air_drop_accuracy
        feedback.ugv_drove_to_location = self.ugv_drove_to_location

        feedback.operational_excellence_percent = self.operational_excellence_percent

        return feedback
Example #4
0
class Odlc(models.Model):
    """Object detection submission for a team."""

    # The mission this is an ODLC for.
    mission = models.ForeignKey('MissionConfig', on_delete=models.CASCADE)
    # The user which submitted and owns this object detection.
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             db_index=True,
                             on_delete=models.CASCADE)
    # Object type.
    odlc_type = models.IntegerField(
        choices=pb_utils.FieldChoicesFromEnum(interop_api_pb2.Odlc.Type))
    # Object location.
    location = models.ForeignKey(GpsPosition,
                                 null=True,
                                 blank=True,
                                 on_delete=models.CASCADE)
    # Object orientation.
    orientation = models.IntegerField(choices=pb_utils.FieldChoicesFromEnum(
        interop_api_pb2.Odlc.Orientation),
                                      null=True,
                                      blank=True)
    # Object shape.
    shape = models.IntegerField(choices=pb_utils.FieldChoicesFromEnum(
        interop_api_pb2.Odlc.Shape),
                                null=True,
                                blank=True)
    # Object background color.
    shape_color = models.IntegerField(choices=pb_utils.FieldChoicesFromEnum(
        interop_api_pb2.Odlc.Color),
                                      null=True,
                                      blank=True)
    # Object alphanumeric.
    alphanumeric = models.TextField(default='', blank=True)
    # Object alphanumeric color.
    alphanumeric_color = models.IntegerField(
        choices=pb_utils.FieldChoicesFromEnum(interop_api_pb2.Odlc.Color),
        null=True,
        blank=True)
    # Free-form object description.
    description = models.TextField(default='', blank=True)
    # Whether judge considers description valid.
    description_approved = models.NullBooleanField()
    # Object is an ADLC submission.
    autonomous = models.BooleanField(default=False)
    # Uploaded object image thumbnail.
    thumbnail = models.ImageField(upload_to='objects', blank=True)
    # Whether judge considers thumbnail valid for object.
    thumbnail_approved = models.NullBooleanField()
    # Time that this object was first created.
    creation_time = models.DateTimeField()
    # Time that this object was last modified.
    last_modified_time = models.DateTimeField()

    def __init__(self, *args, **kwargs):
        super(Odlc, self).__init__(*args, **kwargs)
        if not self.creation_time or not self.last_modified_time:
            self.update_last_modified()

    def update_last_modified(self):
        """Updates timestamps for modification."""
        self.last_modified_time = timezone.now()
        if not self.creation_time:
            self.creation_time = self.last_modified_time

    def similar_orientation(self, other):
        """Compares the orientations for equality.

        Some alphanumerics can have multiple allowed orientations.

        Args:
            other: Another object for which to compare.
        Returns:
            True if the orientations can be considered equal.
        """
        if self.orientation == other.orientation:
            return True

        accepts_any = ['o', 'O', '0']
        if self.alphanumeric in accepts_any:
            return True

        accepts_rotation = [
            'H', 'I', 'N', 'o', 'O', 's', 'S', 'x', 'X', 'z', 'Z', '0', '8'
        ]
        rotated = {
            interop_api_pb2.Odlc.N: interop_api_pb2.Odlc.S,
            interop_api_pb2.Odlc.NE: interop_api_pb2.Odlc.SW,
            interop_api_pb2.Odlc.E: interop_api_pb2.Odlc.W,
            interop_api_pb2.Odlc.SE: interop_api_pb2.Odlc.NW,
            interop_api_pb2.Odlc.S: interop_api_pb2.Odlc.N,
            interop_api_pb2.Odlc.SW: interop_api_pb2.Odlc.NE,
            interop_api_pb2.Odlc.W: interop_api_pb2.Odlc.E,
            interop_api_pb2.Odlc.NW: interop_api_pb2.Odlc.SE,
        }
        if (self.alphanumeric in accepts_rotation
                and rotated[self.orientation] == other.orientation):
            return True

        return False

    def similar_classifications_ratio(self, other):
        """Counts the number of similar classification attributes.

        Args:
            other: Another object for which to compare.
        Returns:
            The ratio of attributes which are the same.
        """
        # Cannot have similar fields with different type objects.
        if self.odlc_type != other.odlc_type:
            return 0

        # Emergent only compares descriptions.
        if self.odlc_type == interop_api_pb2.Odlc.EMERGENT:
            if self.description_approved == other.description_approved:
                return 1
            return 0

        # Compare the fields which require equality.
        direct_compare_fields = [
            'shape', 'shape_color', 'alphanumeric', 'alphanumeric_color'
        ]
        similar_fields = 0
        total_fields = len(direct_compare_fields)
        for field in direct_compare_fields:
            if getattr(self, field) == getattr(other, field):
                similar_fields += 1

        # Compare orientation, accounting for multiple acceptable orientations.
        total_fields += 1
        if self.similar_orientation(other):
            similar_fields += 1

        return float(similar_fields) / total_fields

    def actionable_submission(self, flights):
        """Checks if Odlc meets Actionable Intelligence submission criteria."""
        actionable = False
        if len(flights) > 0:
            flight = flights[0]
            if flight.within(self.creation_time) and \
                flight.within(self.last_modified_time):
                actionable = True
        return actionable