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
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
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)
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')
def test(self): truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) testIns = outgoing_manual() testIns.crop_id = 42 testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'black' self.assertNotEqual(dao.addClassification(testIns), -1) # should now fail to insert a duplicate crop_id self.assertEqual(dao.addClassification(testIns), -1) self.assertIsNotNone(dao.getClassificationByUID(42))
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())
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)
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())
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)
def test(self): # insert some targets dao = OutgoingManualDAO(defaultConfigPath()) truncateTable('outgoing_manual') # make sure if fails when we try and remove on empty table: deleteResult = dao.removeClassification(100) self.assertFalse(deleteResult) 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' resultingId = dao.addClassification(testIns) self.assertIsNot(resultingId, -1) testIns.crop_id = 44 otherId = dao.addClassification(testIns) self.assertNotEqual(otherId, -1) # make sure it doesn't delete everything: deleteResult = dao.removeClassification( otherId + 5) # give bogus id that should fail self.assertFalse(deleteResult) self.assertEqual(len(dao.getAll()), 2) # make sure there's still 2 records deleteResult = dao.removeClassification(resultingId) self.assertTrue(deleteResult) self.assertEqual(len(dao.getAll()), 1) # should still be 1 record left self.assertIsNotNone(dao.getClassification( otherId)) # make sure the otherId wasn't deleted on accident
def delete(self, class_id): # confirm that the X-Manual header was specified manual = checkXManual(request) dao = OutgoingManualDAO( defaultConfigPath()) if manual else OutgoingAutonomousDAO( defaultConfigPath()) if dao.getClassification(class_id) is None: return { 'message': 'Couldnt find classification with id {}'.format(class_id) }, 404 result = dao.removeClassification(class_id) if not result: return { 'message': 'Something went wrong while trying to delete id {} (was it delete by someone else first??)' .format(class_id) }, 500 return {'message': 'success!', 'id': class_id}
def test(self): truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) testIns = outgoing_manual() testIns.crop_id = 42 testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'black' resultingId = dao.addClassification(testIns) self.assertNotEqual(resultingId, -1) insResult = dao.getClassification(resultingId) self.assertIsNotNone(insResult.target) self.assertNotEqual(insResult.target, -1) testIns.crop_id = 43 resultingId = dao.addClassification(testIns) self.assertNotEqual(resultingId, -1) insResult2 = dao.getClassification(resultingId) self.assertEqual(insResult.target, insResult2.target) testIns.crop_id = 44 testIns.alphanumeric = 'C' testIns.submitted = 'submitted' # this wont work -> it should still be unsubmitted resultingId = dao.addClassification(testIns) self.assertNotEqual(resultingId, -1) insResult3 = dao.getClassification(resultingId) self.assertNotEqual(insResult3.target, insResult2.target) # the target id should change on this update testIns.crop_id = 42 testIns.alphanumeric = 'C' testIns.submitted = 'unsubmitted' result = dao.updateClassificationByUID(testIns.crop_id, testIns.toDict()) self.assertNotEqual(result.class_id, -1) self.assertEqual(result.target, insResult3.target)
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()
def test(self): truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) # empty table self.assertIsNone(dao.getUnlocatedClassifications()) # populate with two classifications that need geo, one that doesnt testIns = outgoing_manual() testIns.crop_id = 42 testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'black' resultingId = dao.addClassification(testIns) self.assertNotEqual(resultingId, -1) testIns.crop_id = 43 testIns.background_color = 'orange' resultingId2 = dao.addClassification(testIns) self.assertNotEqual(resultingId2, -1) testIns.crop_id = 44 testIns.latitude = 40.111 testIns.longitude = -111.222 resultingId3 = dao.addClassification(testIns) self.assertNotEqual(resultingId3, -1) unlocated = dao.getUnlocatedClassifications() self.assertIsNotNone(unlocated) self.assertEqual(len(unlocated), 2) # the two models in the list should be our first two inserts and have # crop ids 42 and 43 self.assertLess(unlocated[0].crop_id, 44) self.assertLess(unlocated[1].crop_id, 44)
def getClassificationDAO(isManual): if isManual: return OutgoingManualDAO(defaultConfigPath()) else: return OutgoingAutonomousDAO(defaultConfigPath())
def test(self): truncateTable('cropped_manual') truncateTable('incoming_image') truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) testIns = outgoing_manual() testIns.crop_id = 42 testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'black' self.assertNotEqual(dao.addClassification(testIns), -1) dao = CroppedManualDAO(defaultConfigPath()) model = cropped_manual() model.image_id = 123 model.time_stamp = 1547453775.2 model.cropped_path = '/im/a/totally/real/cropped/path/i/swear.jpg' model.crop_coordinate_br = "(12, 34)" model.crop_coordinate_tl = "(56, 78)" self.assertNotEqual(dao.addImage(model), -1) dao = IncomingImageDAO(defaultConfigPath()) 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 = True model.autonomous_tap = False resultingId = dao.addImage(model) self.assertNotEqual(resultingId, -1) util = UtilDAO(defaultConfigPath()) util.resetManualDB() resultingModel = dao.getImage(resultingId) self.assertIsNotNone(resultingModel) self.assertFalse(resultingModel.manual_tap) self.assertEqual(resultingModel.image_path, model.image_path) self.assertEqual(resultingModel.focal_length, model.focal_length) dao = CroppedManualDAO(defaultConfigPath()) self.assertEqual(len(dao.getAll()), 0) dao = OutgoingManualDAO(defaultConfigPath()) self.assertEqual(len(dao.getAll()), 0)
def test(self): toAvg = [[0, 1.0], [1, 1.1], [2, 1.4]] dao = OutgoingManualDAO(defaultConfigPath()) self.assertAlmostEqual(dao.calcClmnAvg(toAvg, 1), 1.166666667)
def test(self): truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) # at this point we should've already passed a bunch of tests # relating to target submission above, so we can just test the # classification specification feature here 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' firstClass = dao.addClassification(testIns) self.assertNotEqual(firstClass, -1) testIns.crop_id = 43 testIns.latitude = 40.222 testIns.longitude = -111.222 testIns.orientation = 'NE' testIns.background_color = 'orange' secondClass = dao.addClassification(testIns) self.assertNotEqual(secondClass, -1) testIns.crop_id = 44 testIns.latitude = 40.333 testIns.longitude = -111.333 testIns.alphanumeric_color = 'white' thirdClass = dao.addClassification(testIns) self.assertNotEqual(thirdClass, -1) specs = { 'orientation': secondClass, 'crop_id': firstClass, 'alphanumeric_color': thirdClass } classResult = dao.getClassification(secondClass) submissionResult = dao.submitPendingTarget(classResult.target, specs) self.assertIsNotNone(submissionResult) self.assertEqual(submissionResult.orientation, 'NE') self.assertEqual(submissionResult.crop_id, 42) self.assertEqual(submissionResult.alphanumeric_color, 'white') self.assertEqual(submissionResult.background_color, 'orange') self.assertEqual(submissionResult.alphanumeric, 'A') truncateTable('outgoing_manual') ############################################ # test what happens when we put garbage in specs: ############################################ testIns = outgoing_manual() testIns.crop_id = 42 testIns.latitude = 40.111 testIns.longitude = -111.111 testIns.orientation = 'S' testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'Q' testIns.alphanumeric_color = 'black' self.assertNotEqual(dao.addClassification(testIns), -1) testIns.crop_id = 43 testIns.latitude = 40.222 testIns.longitude = -111.222 testIns.orientation = 'W' testIns.background_color = 'orange' secondClass = dao.addClassification(testIns) self.assertNotEqual(secondClass, -1) testIns.crop_id = 44 testIns.latitude = 40.333 testIns.longitude = -111.333 testIns.alphanumeric_color = 'white' self.assertNotEqual(dao.addClassification(testIns), -1) specs = { 'orientation': secondClass, 'crop_id': None, 'alphanumeric_color': 'wasdf' } classResult = dao.getClassification(secondClass) submissionResult = dao.submitPendingTarget(classResult.target, specs) # Even though we fed a bunch of garbage in specs, submission should # still succeed, defaulting to most common value, for the garbage stuff self.assertIsNotNone(submissionResult) self.assertEqual(submissionResult.orientation, 'W') self.assertEqual(submissionResult.alphanumeric_color, 'black') self.assertEqual(submissionResult.background_color, 'orange') self.assertEqual(submissionResult.alphanumeric, 'Q')
def test(self): toMostCommon = [[0, 'A'], [1, 'B'], [3, 'B'], [4, 'C']] dao = OutgoingManualDAO(defaultConfigPath()) self.assertEqual(dao.findMostCommonValue(toMostCommon, 1), 'B') self.assertIsNone(dao.findMostCommonValue([[0, None], [1, None]], 1))
def test(self): dao = OutgoingManualDAO(defaultConfigPath()) self.assertIsNotNone(dao) self.assertIsNotNone(dao.conn)