コード例 #1
0
    def put(self, crop_id):

        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        content = request.get_json()
        if content is None:
            abort(400, 'Must specify values to update')
        if 'image_id' in content:
            abort(400, 'Updating image_id is forbidden!')
        if 'crop_id' in content:
            abort(400, "Updating the crop_id is forbidden")

        dao = CroppedManualDAO(
            defaultConfigPath()) if manual else CroppedAutonomousDAO(
                defaultConfigPath())
        result = dao.updateImage(crop_id, content)
        if result is None:
            return {
                'message':
                'No image with id {} found to update (or was there a server error?)'
                .format(crop_id)
            }, 404
        else:
            return jsonify(result.toJsonResponse())
コード例 #2
0
    def get(self):
        manual = checkXManual(request)
        dao = SubmittedTargetDAO(defaultConfigPath())

        resultingClassifications = dao.getAllTargets(not manual)
        if resultingClassifications is None or not resultingClassifications:
            return {
                'message':
                'Failed to retrieve any submitted targets (Have any been submitted?)'
            }, 404

        resultingClassifications = [
            classification.toDict()
            for classification in resultingClassifications
        ]

        croppedDao = CroppedManualDAO(
            defaultConfigPath()) if manual else CroppedAutonomousDAO(
                defaultConfigPath())

        for target in resultingClassifications:
            # for each of these targets we need to try and get the crop_id for it from the crop_path
            # crop_id is much more useful client-side than crop_path
            croppedImg = croppedDao.getImageWithCropPath(target['crop_path'])
            if croppedImg is not None:
                target['crop_id'] = croppedImg.crop_id

        return jsonify(resultingClassifications)
コード例 #3
0
    def post(self):

        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        prevId = -1
        if 'X-Crop-Id' in request.headers:
            prevId = request.headers.get('X-Crop-Id')
        else:
            abort(400, "Need to specify header 'X-Crop-Id'!")

        dao = OutgoingManualDAO(
            defaultConfigPath()) if manual else OutgoingAutonomousDAO(
                defaultConfigPath())

        outgoingIn = outgoing_manual(
            json=request.get_json()) if manual else outgoing_autonomous(
                json=request.get_json())
        outgoingIn.crop_id = prevId
        resultingId = dao.upsertClassification(outgoingIn)

        if resultingId == -1:
            return {
                'message':
                'Failed to insert classification into outgoing table'
            }, 500

        response = make_response(
            jsonify({
                'message': 'success!',
                'id': resultingId
            }))
        response.headers['X-Class-Id'] = resultingId
        return response
コード例 #4
0
    def run(self):

        # wait until we get our first gps coordinate
        # remember - the ros_handler code does not insert a gps record
        #           into the table until the gps has a fix
        haveGps = False
        print("waiting for gps")
        while not haveGps and not self._should_shutdown:
            # check for any entries by just getting all
            dao = IncomingGpsDAO(defaultConfigPath())
            results = dao.getAll()
            dao.close()
            if results is None:
                time.sleep(1)
            else:
                haveGps = True  # we've got something!

        print(
            "Have gps! Using the first coordinate as our ground station. Starting geolocation..."
        )

        # get the coordinates from the first recorded gps measurement
        # this will be the coordinates we use for the groundstation
        # in geolocation
        dao = IncomingGpsDAO(defaultConfigPath())
        groundstationGps = dao.getFirst()
        dao.close()

        # now we can run stuff
        geo = targetGeolocation(groundstationGps.lat, groundstationGps.lon)
        while not self._should_shutdown:

            # lets deal with manual classifications in the queue
            dao = OutgoingManualDAO(defaultConfigPath())
            classifications = dao.getUnlocatedClassifications()
            dao.close()

            if classifications is not None:
                for classification in classifications:
                    self.processManualGeolocation(geo, classification)

            # now lets do autonomous
            dao = OutgoingAutonomousDAO(defaultConfigPath())
            classifications = dao.getUnlocatedClassifications()
            dao.close()

            if classifications is not None:
                for classification in classifications:
                    self.processAutonomousGeolocation(geo, classification)

            time.sleep(1)
コード例 #5
0
    def get(self, crop_id):
        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        dao = CroppedManualDAO(
            defaultConfigPath()) if manual else CroppedAutonomousDAO(
                defaultConfigPath())
        image = dao.getImage(crop_id)

        if image is None:
            return {
                'message': 'Failed to locate cropped id {}'.format(crop_id)
            }, 404
        return jsonify(image.toJsonResponse())
コード例 #6
0
    def get(self):

        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        dao = CroppedManualDAO(
            defaultConfigPath()) if manual else CroppedAutonomousDAO(
                defaultConfigPath())
        croppedList = dao.getAll()

        if croppedList == None or not croppedList:
            return {'message': 'Cropped table is empty!'}, 404

        exportable = [img.toJsonResponse() for img in croppedList]
        return jsonify(exportable)
コード例 #7
0
    def get(self, class_id):

        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        dao = OutgoingManualDAO(
            defaultConfigPath()) if manual else OutgoingAutonomousDAO(
                defaultConfigPath())
        result = dao.getClassification(class_id)
        if result is None:
            return {
                'message':
                'Failed to locate classification with id {}'.format(class_id)
            }, 404
        return jsonify(result.toDict())
コード例 #8
0
    def get(self):
        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        # Get content:
        dao = CroppedManualDAO(
            defaultConfigPath()) if manual else CroppedAutonomousDAO(
                defaultConfigPath())
        image = dao.getNextImage()

        # response validation
        if image is None:
            return {'message': 'Failed to locate untapped image'}, 404

        # success!
        return cropImageSender(image, image.cropped_path)
コード例 #9
0
    def test(self):
        truncateTable('outgoing_manual')
        dao = OutgoingManualDAO(defaultConfigPath())

        # see how it works on an empty table
        self.assertIsNone(dao.submitAllPendingTargets())

        # this will insert records for 2 different targets from 3 classifications:
        testIns = outgoing_manual()
        testIns.crop_id = 42
        testIns.shape = 'circle'
        testIns.background_color = 'white'
        testIns.alphanumeric = 'A'
        testIns.alphanumeric_color = 'black'
        dao.addClassification(testIns)

        testIns.crop_id = 43
        dao.addClassification(testIns)

        testIns.crop_id = 44
        testIns.alphanumeric = 'C'
        testIns.submitted = 'submitted'  # this wont work -> it should still be unsubmitted
        dao.addClassification(testIns)

        result = dao.submitAllPendingTargets()
        self.assertIsNotNone(result)
        self.assertEqual(len(result), 2)  # should have 2 targets
コード例 #10
0
ファイル: auvsi_odlc_test.py プロジェクト: BYU-AUVSI/imaging
    def test(self):
        testIns = outgoing_manual()
        testIns.crop_id = 42
        testIns.type = 'standard'
        testIns.orientation = 'NE'
        testIns.shape = 'star'
        testIns.background_color = 'blue'
        testIns.alphanumeric = 'A'
        testIns.alphanumeric_color = 'purple'
        testIns.latitude  = 40.11111
        testIns.longitude = -111.222222

        truncateTable('outgoing_manual')
        dao = OutgoingManualDAO(defaultConfigPath())

        id = dao.addClassification(testIns)
        self.assertNotEqual(id, -1)

        model = dao.getClassification(id)
        self.assertIsNotNone(model)

        classOut = dao.submitPendingTarget(model.target)
        self.assertIsNotNone(classOut)

        imgPath = os.path.dirname(os.path.realpath(__file__)) + '/assets/star.png'
        
        targetOut = submitted_target(outgoingManualOrAutonomous=classOut, autonomous_in=False)
        targetOut.crop_path = imgPath
        auvsiDao = AuvsiOdlcDao()
        auvsiDao.addTarget(targetOut)
コード例 #11
0
    def test(self):
        model = incoming_image()
        model.time_stamp = 1547453775.2
        model.focal_length = 16.0
        model.image_path = '/im/a/totally/real/path/i/swear.jpg'
        model.manual_tap = False
        model.autonomous_tap = False

        truncateTable('incoming_image')
        dao = IncomingImageDAO(defaultConfigPath())
        self.assertIsNotNone(dao)

        # test with empty table

        self.assertIsNone(dao.getImage(1))

        resultingId = dao.addImage(model)
        self.assertNotEqual(resultingId, -1)

        resultingModel = dao.getImage(resultingId)

        self.assertIsNotNone(resultingModel)
        self.assertAlmostEqual(resultingModel.time_stamp, model.time_stamp)
        self.assertEqual(resultingModel.focal_length, model.focal_length)
        self.assertEqual(resultingModel.image_path, model.image_path)
        self.assertEqual(resultingModel.manual_tap, model.manual_tap)
        self.assertEqual(resultingModel.autonomous_tap, model.autonomous_tap)
コード例 #12
0
    def test(self):
        truncateTable('submitted_target')
        dao = SubmittedTargetDAO(defaultConfigPath())

        # none should fail to insert
        result = dao.upsertTarget(None)
        self.assertIsNotNone(result)
        self.assertEqual(result, -1)

        testIns = submitted_target()
        testIns.shape = 'square'
        testIns.type = 'standard'
        testIns.crop_path = '/another/totally/real/crop/path/ikr.jpg'
        testIns.background_color = 'white'
        testIns.alphanumeric = "T"
        testIns.alphanumeric_color = "orange"

        # should fail when we try and upsert an image that doesn't have a target or autonomous field
        self.assertEqual(dao.upsertTarget(testIns), -1)

        testIns.autonomous = False
        # should fail when we try and upsert an image that doesn't have a target field
        self.assertEqual(dao.upsertTarget(testIns), -1)

        testIns.target = 10
        self.assertNotEqual(dao.upsertTarget(testIns), -1)
コード例 #13
0
    def test(self):
        truncateTable('submitted_target')
        dao = SubmittedTargetDAO(defaultConfigPath())

        testIns = submitted_target()
        testIns.shape = 'square'
        testIns.type = 'standard'
        testIns.crop_path = '/another/totally/real/crop/path/ikr.jpg'
        testIns.background_color = 'white'
        testIns.alphanumeric = "T"
        testIns.alphanumeric_color = "orange"
        testIns.autonomous = False
        testIns.target = 10

        self.assertNotEqual(dao.upsertTarget(testIns), -1)

        resultingModel = dao.getTarget(10, False)

        self.assertIsNotNone(resultingModel)
        self.assertEqual(resultingModel.target, 10)
        self.assertFalse(resultingModel.autonomous)
        self.assertEqual(resultingModel.type, 'standard')
        self.assertEqual(resultingModel.crop_path,
                         '/another/totally/real/crop/path/ikr.jpg')
        self.assertEqual(resultingModel.shape, 'square')
        self.assertEqual(resultingModel.background_color, 'white')
        self.assertEqual(resultingModel.alphanumeric, 'T')
        self.assertEqual(resultingModel.alphanumeric_color, 'orange')
        self.assertEqual(resultingModel.submitted, 'pending')
        self.assertIsNone(resultingModel.latitude)
        self.assertIsNone(resultingModel.longitude)
        self.assertIsNone(resultingModel.orientation)
コード例 #14
0
    def put(self, class_id):

        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        dao = OutgoingManualDAO(
            defaultConfigPath()) if manual else OutgoingAutonomousDAO(
                defaultConfigPath())
        result = dao.updateClassification(class_id, request.get_json())
        if result is None:
            return {
                'message':
                'No image with id {} found with a classification to update or your input was invalid (or was there a server error?)'
                .format(class_id)
            }, 404
        else:
            return jsonify(result.toDict())
コード例 #15
0
    def get(self):
        # confirm that the X-Manual header was specified
        manual = checkXManual(request)

        outgoingList = []
        dao = OutgoingManualDAO(
            defaultConfigPath()) if manual else OutgoingAutonomousDAO(
                defaultConfigPath())
        outgoingList = dao.getAll()

        if not outgoingList:
            return {'message': 'Outgoing table is empty!'}, 404

        exportable = [
            classification.toDict() for classification in outgoingList
        ]
        return jsonify(exportable)
コード例 #16
0
    def get(self, image_id):
        dao = IncomingImageDAO(defaultConfigPath())
        image = dao.getImage(image_id)

        if image is None:
            return {
                'message': 'Failed to locate raw id {}'.format(image_id)
            }, 404
        return jsonify(image.toDict())
コード例 #17
0
    def get(self, id):
        dao = IncomingStateDAO(defaultConfigPath())
        state = dao.getStateById(id)

        if state is None:
            return {
                'message': 'Failed to locate state entry with id {}'.format(id)
            }, 404

        return jsonify(state.toDict())
コード例 #18
0
    def get(self, image_id):
        dao = IncomingImageDAO(defaultConfigPath())
        image = dao.getImage(image_id)
        if image is None:
            return {
                'message': 'Failed to locate raw id {}'.format(image_id)
            }, 404

        # otherwise lets send the image::
        return rawImageSender(image.image_id, image.image_path)
コード例 #19
0
ファイル: gps_handler.py プロジェクト: BYU-AUVSI/imaging
    def get(self, id):
        dao = IncomingGpsDAO(defaultConfigPath())
        gps = dao.getGpsById(id)

        # response validation
        if gps is None:
            return {'message': 'Failed to locate gps id {}'.format(id)}, 404

        # success!
        return jsonify(gps.toDict())
コード例 #20
0
    def processGeolocation(self, geolocator, classification, croppedImg):
        """
        Geolocation processing stuff common to both manual and autonomous.
        Called only by the processManualGeolocation and processAutonomousGeolocation
        methods
        """
        dao = IncomingImageDAO(defaultConfigPath())
        rawImg = dao.getImage(croppedImg.image_id)
        dao.close()
        if rawImg is None:
            print(
                "Failed to find raw image {} for autonomous cropped image {}!".
                format(croppedImg.image_id, croppedImg.crop_id))
            return None

        dao = IncomingGpsDAO(defaultConfigPath())
        gpsRaw = dao.getGpsByClosestTS(rawImg.time_stamp)
        dao.close()

        if gpsRaw is None:
            print("Failed to find gps measurement close to raw timestamp {}!".
                  format(rawImg.time_stamp))
            return None

        dao = IncomingStateDAO(defaultConfigPath())
        stateRaw = dao.getStateByClosestTS(rawImg.time_stamp)
        dao.close()

        if stateRaw is None:
            print(
                "Failed to find state measurement close to raw timestamp {}!".
                format(rawImg.time_stamp))
            return None

        # lat, lon = geolocator.calculate_geolocation(gpsRaw.lat, gpsRaw.lon, gpsRaw.alt, stateRaw.roll, stateRaw.pitch, stateRaw.yaw, croppedImg.crop_coordinate_tl.x, croppedImg.crop_coordinate_tl.y, croppedImg.crop_coordinate_br.x, croppedImg.crop_coordinate_br.y)
        lat, lon = geolocator.calculate_geolocation(
            gpsRaw.lat, gpsRaw.lon, -stateRaw.position[2], stateRaw.roll,
            stateRaw.pitch, stateRaw.yaw, croppedImg.crop_coordinate_tl.x,
            croppedImg.crop_coordinate_tl.y, croppedImg.crop_coordinate_br.x,
            croppedImg.crop_coordinate_br.y)

        return {'latitude': lat, 'longitude': lon}
コード例 #21
0
    def get(self, ts):
        dao = IncomingStateDAO(defaultConfigPath())
        state = dao.getStateByClosestTS(ts)

        if state is None:
            return {
                'message':
                'Failed to get closest state measurement by time_stamp. Either: a) the table is empty or b) the time_stampe is < all timestamps in the table'
            }, 404

        return jsonify(state.toDict())
コード例 #22
0
    def processAutonomousGeolocation(self, geolocator, classification):
        """
        See documentation for processManualGeolocation function. This is the same
        thing but autonomous.
        """
        dao = CroppedAutonomousDAO(defaultConfigPath())
        croppedImg = dao.getImage(classification.crop_id)
        dao.close()
        if croppedImg is None:
            print(
                "Failed to find cropped image {} for autonomous classification {}!"
                .format(classification.crop_id, classification.class_id))
            return

        updateDict = self.processGeolocation(geolocator, classification,
                                             croppedImg)

        dao = OutgoingAutonomousDAO(defaultConfigPath())
        dao.updateClassification(classification.class_id, updateDict)
        dao.close()
コード例 #23
0
    def processManualGeolocation(self, geolocator, classification):
        """
        Process an unlocated manual classification waiting in the queue. Basically
        this involves getting a bunch of information from a lot of the other database
        tables, and then using it as input to the actual geolocation algorithm.
        If successful, this will update the given classification with the calculated
        coordinates. Here's what we use:

            cropped_manual -> image_id, crop_coordinates
            incoming_image -> image timestamp
            incoming_gps   -> mav gps coordinates at incoming_image timestamp
            incoming_gps   -> mav state info at incoming_image timestamp

        @type geolocator: targetGeolocation
        @param geolocator: An instance of the targetGeolocation class, already 
            initialized
        
        @type classification: outgoing_manual
        @param classification: A manual classification that has no geolocation 
            coordinates yet.
        """

        # now get all the info about gps, state and crop
        # as we do this we need to check that we actually get
        # data back from our queries. if we dont, then just
        # skip this classification
        dao = CroppedManualDAO(defaultConfigPath())
        croppedImg = dao.getImage(classification.crop_id)
        dao.close()
        if croppedImg is None:
            print(
                "Failed to find cropped image {} for manual classification {}!"
                .format(classification.crop_id, classification.class_id))
            return

        updateDict = self.processGeolocation(geolocator, classification,
                                             croppedImg)

        dao = OutgoingManualDAO(defaultConfigPath())
        dao.updateClassification(classification.class_id, updateDict)
        dao.close()
コード例 #24
0
def writeTargetToODLCFile(target, manual):
    imagePath = None

    # we have a crop_id from the classification generated for submission
    # now we need to take that and grab the cropped path
    croppedDao = CroppedManualDAO(
        defaultConfigPath()) if manual else CroppedAutonomousDAO(
            defaultConfigPath())
    croppedInfo = croppedDao.getImage(target.crop_id)

    if croppedInfo is None:
        return None
    imagePath = croppedInfo.cropped_path

    prettyTargetOut = submitted_target(outgoingManualOrAutonomous=target,
                                       autonomous_in=(not manual))
    prettyTargetOut.crop_path = imagePath

    auvsiDao = AuvsiOdlcDao()
    auvsiDao.addTarget(prettyTargetOut)
    return prettyTargetOut
コード例 #25
0
    def test(self):
        # insert some targets
        dao = OutgoingManualDAO(defaultConfigPath())

        truncateTable('outgoing_manual')
        # see what it does with an empty table:
        self.assertIsNone(dao.submitPendingTarget(1))

        testIns = outgoing_manual()
        testIns.crop_id = 42
        testIns.latitude = 40.111
        testIns.longitude = -111.111
        testIns.orientation = 'N'
        testIns.shape = 'circle'
        testIns.background_color = 'white'
        testIns.alphanumeric = 'A'
        testIns.alphanumeric_color = 'black'
        self.assertIsNot(dao.addClassification(testIns), -1)

        testIns.crop_id = 43
        testIns.latitude = 40.222
        testIns.longitude = -111.222
        testIns.orientation = 'NE'
        testIns.background_color = 'orange'
        self.assertIsNot(dao.addClassification(testIns), -1)

        testIns.crop_id = 44
        testIns.latitude = 40.333
        testIns.longitude = -111.333
        testIns.alphanumeric_color = 'white'
        resultingId = dao.addClassification(testIns)
        self.assertIsNot(resultingId, -1)

        classResult = dao.getClassification(resultingId)
        submissionResult = dao.submitPendingTarget(classResult.target)

        self.assertAlmostEqual(submissionResult.latitude, 40.222)
        self.assertAlmostEqual(submissionResult.longitude, -111.222)
        self.assertEqual(submissionResult.orientation, 'NE')
        self.assertEqual(submissionResult.background_color, 'orange')
        self.assertEqual(submissionResult.alphanumeric_color, 'black')
        self.assertEqual(submissionResult.alphanumeric, 'A')
        self.assertEqual(submissionResult.shape, 'circle')

        # make sure that when we submit another classification that belongs
        # to the same target that its submitted state automatically goes to
        # 'inherited_submission'
        testIns.crop_id = 45
        resultingId = dao.addClassification(testIns)
        self.assertNotEqual(resultingId, -1)
        classResult = dao.getClassification(resultingId)
        self.assertIsNotNone(classResult)
        self.assertEqual(classResult.submitted, 'inherited_submission')
コード例 #26
0
    def test(self):
        truncateTable('incoming_image')
        dao = IncomingImageDAO(defaultConfigPath())
        self.assertIsNotNone(dao)

        # test with empty table
        self.assertIsNone(dao.getNextImage(True))
        self.assertIsNone(dao.getNextImage(False))

        model = incoming_image()
        model.time_stamp = 1547453775.2
        model.focal_length = 16.0
        model.image_path = '/im/a/totally/real/path/i/swear.jpg'
        model.manual_tap = False
        model.autonomous_tap = False
        resultingId = dao.addImage(model)
        self.assertNotEqual(resultingId, -1)

        # identical timestamps should make no difference
        model.focal_length = 16.0
        model.image_path = '/im/a/totally/real/path/2/i/swear.jpg'
        resultingId2 = dao.addImage(model)
        self.assertNotEqual(resultingId2, -1)

        resultModel = dao.getNextImage(True)
        self.assertIsNotNone(resultModel)
        self.assertEqual(resultModel.image_id, resultingId)
        self.assertTrue(resultModel.manual_tap)
        self.assertFalse(resultModel.autonomous_tap)

        resultModel = dao.getNextImage(True)
        self.assertIsNotNone(resultModel)
        self.assertEqual(resultModel.image_id, resultingId2)
        self.assertTrue(resultModel.manual_tap)
        self.assertFalse(resultModel.autonomous_tap)

        resultModel = dao.getNextImage(False)
        self.assertIsNotNone(resultModel)
        self.assertEqual(resultModel.image_id, resultingId)
        self.assertTrue(resultModel.manual_tap)
        self.assertTrue(resultModel.autonomous_tap)

        resultModel = dao.getNextImage(True)
        self.assertIsNone(resultModel)

        resultModel = dao.getNextImage(False)
        self.assertIsNotNone(resultModel)
        self.assertEqual(resultModel.image_id, resultingId2)
        self.assertTrue(resultModel.manual_tap)
        self.assertTrue(resultModel.autonomous_tap)

        resultModel = dao.getNextImage(False)
        self.assertIsNone(resultModel)
コード例 #27
0
    def get(self, target_id):
        manual = checkXManual(request)
        dao = SubmittedTargetDAO(defaultConfigPath())

        resultTarget = dao.getTarget(target_id, (not manual))
        if resultTarget is None:
            return {
                'message': 'Failed to retrieve target {}'.format(target_id)
            }, 404

        # get the crop id from the path the target is using.
        # the crop_id is much more useful client-size than a crop_path
        outDict = resultTarget.toDict()
        croppedDao = CroppedManualDAO(
            defaultConfigPath()) if manual else CroppedAutonomousDAO(
                defaultConfigPath())
        croppedImg = croppedDao.getImageWithCropPath(outDict['crop_path'])
        if croppedImg is not None:
            outDict['crop_id'] = croppedImg.crop_id

        return jsonify(outDict)
コード例 #28
0
ファイル: gps_handler.py プロジェクト: BYU-AUVSI/imaging
    def get(self, ts):
        dao = IncomingGpsDAO(defaultConfigPath())
        gps = dao.getGpsByClosestTS(ts)

        # response validation:
        if gps is None:
            return {
                'message':
                'Failed to get closest gps by time_stamp. Is the table empty?'
            }, 404
        # success!
        return jsonify(gps.toDict())
コード例 #29
0
    def test(self):
        model = incoming_gps()
        model.time_stamp = 1547453775.2
        model.lat = 40.111
        model.lon = -111.222
        model.alt = 1234.5

        truncateTable('incoming_gps')
        dao = IncomingGpsDAO(defaultConfigPath())

        resultingId = dao.addGps(model)
        self.assertIsNotNone(resultingId)
        self.assertNotEqual(resultingId, -1)
コード例 #30
0
    def test(self):
        model = incoming_state()
        model.time_stamp = 1547453775.2
        model.roll = 40.111
        model.pitch = -111.222
        model.yaw = 12.3

        truncateTable('incoming_state')
        dao = IncomingStateDAO(defaultConfigPath())
        self.assertIsNotNone(dao)
        resultingId = dao.addState(model)
        self.assertIsNotNone(resultingId)
        self.assertNotEqual(resultingId, -1)