def test_throws_when_scene_is_missing_status(self): mangled_scene = json.loads(RESPONSE_SCENE_ACTIVE) del mangled_scene['properties']['status'] self.mock_requests.get('/planet/planetscope/test-scene-id', json=mangled_scene) with self.assertRaises(scenes.ValidationError): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_throws_when_scene_has_invalid_resolution(self): mangled_scene = json.loads(RESPONSE_SCENE_ACTIVE) mangled_scene['properties']['resolution'] = None self.mock_requests.get('/planet/planetscope/test-scene-id', json=mangled_scene) with self.assertRaises(scenes.ValidationError): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_throws_when_scene_has_invalid_tide_max(self): mangled_scene = json.loads(RESPONSE_SCENE_ACTIVE) mangled_scene['properties']['MaximumTide24Hours'] = 'whee' self.mock_requests.get('/planet/planetscope/test-scene-id', json=mangled_scene) with self.assertRaises(scenes.ValidationError): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_throws_when_scene_id_is_malformed(self): malformed_ids = ( 'lolwut', 'planetnope:foobar', ) for malformed_id in malformed_ids: with self.assertRaises(scenes.MalformedSceneID): scenes.get(malformed_id, 'test-planet-api-key')
def test_calls_correct_url_for_rapideye_scene(self): self.mock_requests.get('/planet/rapideye/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scenes.get('rapideye:test-scene-id', 'test-planet-api-key') uri, params = self.mock_requests.request_history[0].url.split('?', 1) self.assertEqual( 'https://test-catalog-host.localdomain/planet/rapideye/test-scene-id', uri) self.assertEqual({'PL_API_KEY=test-planet-api-key', 'tides=True'}, set(params.split('&')))
def test_returns_correct_geotiff_single_band_urls(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') # FIXME -- if Planet's API ever offers Landsat retrieval... self.assertIsNone(scene.geotiff_coastal) self.assertIsNone(scene.geotiff_swir1)
def test_returns_correct_uri(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertEqual( 'https://test-catalog-host.localdomain/planet/planetscope/test-scene-id', scene.uri)
def test_returns_correct_geometry(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertIsInstance(scene.geometry, dict) self.assertIsInstance(scene.geometry.get('coordinates'), list) self.assertEqual([[[115.78907135188213, 26.67939763560932], [115.78653934657243, 26.905071315070465], [116.01004245933433, 26.90679345550323], [115.95815780815747, 26.680701401397425], [115.78907135188213, 26.67939763560932]]], scene.geometry.get('coordinates'))
def forward_to_geotiff(scene_id: str): planet_api_key = flask.request.args.get('planet_api_key') if not planet_api_key: return 'Missing `planet_api_key` parameter', 400 user = getattr(flask.request, 'user', None) user_id = user.user_id if user else None try: scene = _scenes.get(scene_id, planet_api_key) geotiff_url = _scenes.activate(scene, planet_api_key, user_id) except _scenes.NotFound: return 'Cannot download: Scene `{}` not found'.format(scene_id), 404 except (_scenes.CatalogError, _scenes.MalformedSceneID) as err: return 'Cannot download: {}'.format(err), 500 if geotiff_url: return flask.redirect(geotiff_url) return flask.render_template('download_scene.jinja2', scene_id=scene_id), 202
def test_saves_scene_to_database(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertTrue(self._mockdb.executed)
def test_gracefully_handles_database_errors(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) self._mockdb.raise_on_execute() with self.assertRaises(DatabaseError): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_returns_correct_capture_date(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertEqual('2017-01-20T00:00:00+00:00', scene.capture_date.isoformat())
def test_fetches_scenes(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertIsInstance(scene, scenes.Scene)
def test_returns_correct_geotiff_multispectral_url_for_inactive_scene( self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertIsNone(scene.geotiff_multispectral)
def test_returns_correct_cloud_cover(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertEqual(1.47, scene.cloud_cover)
def test_throws_when_scene_does_not_exist(self): self.mock_requests.get('/planet/planetscope/test-scene-id', status_code=404, text='wat') with self.assertRaises(scenes.NotFound): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_throws_when_not_permitted(self): self.mock_requests.get('/planet/planetscope/test-scene-id', status_code=401, text='oopsie') with self.assertRaises(scenes.NotPermitted): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_throws_when_catalog_throws(self): self.mock_requests.get('/planet/planetscope/test-scene-id', status_code=500, text='oh noes') with self.assertRaises(scenes.CatalogError): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def test_throws_when_catalog_is_unreachable(self): with unittest.mock.patch('requests.get') as stub: stub.side_effect = ConnectionError() with self.assertRaises(scenes.CatalogError): scenes.get('planetscope:test-scene-id', 'test-planet-api-key')
def create(user_id: str, scene_id: str, service_id: str, job_name: str, planet_api_key: str) -> Job: log = logging.getLogger(__name__) log.info('Job service create', action='service job create', actor=user_id) # Fetch prerequisites try: algorithm = algorithms.get(service_id) scene = scenes.get(scene_id, planet_api_key) scenes.activate(scene, planet_api_key, user_id) except (algorithms.NotFound, algorithms.ValidationError, scenes.MalformedSceneID, scenes.CatalogError, scenes.NotFound, scenes.NotPermitted, scenes.ValidationError) as err: log.error('Preprocessing error: %s', err) raise PreprocessingError(err) # Determine GeoTIFF URLs. if scene.platform in ('rapideye', 'planetscope'): geotiff_filenames = ['multispectral.TIF'] geotiff_urls = [scenes.create_download_url(scene.id, planet_api_key)] elif scene.platform == 'landsat': geotiff_filenames = ['coastal.TIF', 'swir1.TIF'] geotiff_urls = [scene.geotiff_coastal, scene.geotiff_swir1] else: raise PreprocessingError(message='Unexpected platform') # Dispatch to Piazza try: log.info('Dispatching <scene:%s> to <algo:%s>', scene_id, algorithm.name) cli_cmd = _create_algorithm_cli_cmd(algorithm.interface, geotiff_filenames, scene.platform) job_id = piazza.execute( algorithm.service_id, { 'body': { 'content': json.dumps({ 'cmd': cli_cmd, 'inExtFiles': geotiff_urls, 'inExtNames': geotiff_filenames, 'outGeoJson': ['shoreline.geojson'], 'userID': user_id, }), 'type': 'body', 'mimeType': 'application/json', }, }) except piazza.Error as err: log.error('Could not execute via Piazza: %s', err) raise # Record the data log.debug('Saving job record <%s>', job_id) conn = db.get_connection() transaction = conn.begin() try: db.jobs.insert_job( conn, algorithm_id=algorithm.service_id, algorithm_name=algorithm.name, algorithm_version=algorithm.version, job_id=job_id, name=job_name, scene_id=scene_id, status=piazza.STATUS_PENDING, user_id=user_id, tide=scene.tide, tide_min_24h=scene.tide_min, tide_max_24h=scene.tide_max, ) db.jobs.insert_job_user( conn, job_id=job_id, user_id=user_id, ) transaction.commit() except db.DatabaseError as err: transaction.rollback() log.error('Could not save job to database') db.print_diagnostics(err) raise finally: conn.close() return Job( algorithm_name=algorithm.name, algorithm_version=algorithm.version, created_by=user_id, created_on=datetime.utcnow(), geometry=scene.geometry, job_id=job_id, name=job_name, scene_time_of_collect=scene.capture_date, scene_sensor_name=scene.sensor_name, scene_id=scene_id, status=piazza.STATUS_PENDING, tide=scene.tide, tide_min_24h=scene.tide_min, tide_max_24h=scene.tide_max, )
def test_returns_correct_sensor_name(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertEqual('test-sensor-name', scene.sensor_name)
def test_returns_correct_resolution(self): self.mock_requests.get('/planet/planetscope/test-scene-id', text=RESPONSE_SCENE_INACTIVE) scene = scenes.get('planetscope:test-scene-id', 'test-planet-api-key') self.assertEqual(4, scene.resolution)