def _response_to_query(query, reply_to, subject=None, url=None): """ Takes a parsed query string fetches the forecast, compresses it and replies to the sender. Parameters ---------- query : dict An dictionary query reply_to : string The email address of the sender. The compressed forecast is sent to this person. subject : string (optional) The subject of the email that is sent. If none, a summary of the query is used. url : string (optional) A path to the forecast that should be used. This is mostly used for debugging and testing. Returns ---------- forecast_attachment : file-like A file-like object holding the forecast that was sent """ compressed_forecast = process_query(query, url=url) logging.debug("Compressed Size: %d" % len(compressed_forecast)) # create a file-like forecast attachment logging.debug('Obtained the forecast') file_fmt = '%Y-%m-%d_%H%m' filename = datetime.datetime.today().strftime(file_fmt) filename = '_'.join([query['type'], query['model'], filename]) if query.get('send-image', False): logging.debug('Sending an image of the forecast') fcst = compress.decompress_dataset(compressed_forecast) visualize.plot_spot(fcst) import matplotlib.pyplot as plt png = BytesIO() plt.savefig(png) png.seek(0) attachments = {'%s.png' % filename: png} else: logging.debug('Sending the compressed forecasts') forecast_attachment = BytesIO(compressed_forecast) ext = decide_extension(reply_to) attachment_name = '%s.%s' % (filename, ext) logging.debug("Attaching forecast as %s" % attachment_name) attachments = {attachment_name: forecast_attachment} # Make sure the forecast file isn't too large for sailmail if 'sailmail' in reply_to and len(compressed_forecast) > 25000: raise utils.BadQuery("Forecast was too large (%d bytes) for sailmail!" % len(compressed_forecast)) # creates the new mime email weather_email = emaillib.create_email(reply_to, _query_email, _email_body, subject=subject or query_summary(query), attachments=attachments) return weather_email
def test_version_assert(self): # create a forecast that looks like its from a newer version # and make sure an assertion is raised. ds = create_data() original_version = compress._VERSION compress._VERSION = np.array(compress._VERSION + 1, dtype=np.uint8) beaufort = compress.compress_dataset(ds) compress._VERSION = original_version self.assertRaises(ValueError, lambda: compress.decompress_dataset(beaufort))
def main(): fcst = test_data() fcst = compress.decompress_dataset(compress.compress_dataset(fcst)) for vn in ['current', 'wind']: variable = query_utils.get_variable(vn) if isinstance(variable, schemes.VelocityVariable): interactive.InteractiveVelocity(fcst, variable) plt.show()
def test_compress_dataset(self): ds = create_data() compressed = compress.compress_dataset(ds) actual = compress.decompress_dataset(compressed) np.testing.assert_allclose(actual['x_wind'].values, ds['x_wind'].values, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(actual['y_wind'].values, ds['y_wind'].values, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(actual['air_pressure_at_sea_level'].values, ds['air_pressure_at_sea_level'].values, atol=1e-4, rtol=1e-4) # pass it through the system again, it should be idempotent. compressed = compress.compress_dataset(ds) actual = compress.decompress_dataset(compressed) np.testing.assert_allclose(actual['x_wind'].values, ds['x_wind'].values, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(actual['y_wind'].values, ds['y_wind'].values, atol=1e-4, rtol=1e-4) np.testing.assert_allclose(actual['air_pressure_at_sea_level'].values, ds['air_pressure_at_sea_level'].values, atol=1e-4, rtol=1e-4)
def test_query(self): some_email = "*****@*****.**" msg = emaillib.create_email( to="*****@*****.**", fr=some_email, body="send GFS:30s,35s,175e,175w|0.5,0.5|0,3..12|WIND" ) emails = [] # use a mocked emaillib to make sure an email was sent with self.get_request(emails) as request: request.process_email(msg.as_string(), fail_hard=True) assert len(emails) == 1 parser = Parser.Parser() email = parser.parsestr(emails.pop().args[0].as_string()) # make sure we are sending responses from ensembleweather self.assertEqual(email["From"], "*****@*****.**") # make sure we are responding to the right person self.assertEqual(email["To"], some_email) # here we pull out the expected attachment body, attach = email.get_payload() bytes = attach.get_payload() # inflate it back into an xray Dataset fcst = compress.decompress_dataset(base64.b64decode(bytes)) # make sure we're actually compressing the file. self.assertLessEqual(len(bytes), len(zlib.compress(fcst.to_netcdf()))) # make sure the lats are what we expect expected_lats = np.linspace(-35.0, -30.0, 11) np.testing.assert_array_equal(fcst["latitude"].values, expected_lats) self.assertEqual(fcst["latitude"].attrs["units"].lower(), "degrees north") # and the lons expected_lons = np.mod(np.linspace(175.0, 185.0, 21) + 180, 360) - 180 np.testing.assert_array_equal(fcst["longitude"].values, expected_lons) self.assertEqual(fcst["longitude"].attrs["units"].lower(), "degrees east") # and the time expected_time = np.linspace(0, 12, 5) expected_time = xray.conventions.decode_cf_datetime(expected_time, fcst["time"].encoding["units"]) np.testing.assert_array_equal(fcst["time"].values, expected_time) self.assertIn("hours", fcst["time"].encoding["units"])
def test_spot_forecast(self): some_email = "*****@*****.**" msg = emaillib.create_email( to="*****@*****.**", fr=some_email, body="send spot:gefs:8.53S,115.54E|8,6|wind" ) emails = [] # use a mocked emaillib to make sure an email was sent with self.get_request(emails) as request: request.process_email(msg.as_string(), fail_hard=True) assert len(emails) == 1 parser = Parser.Parser() email = parser.parsestr(emails.pop().args[0].as_string()) # make sure we are sending responses from ensembleweather self.assertEqual(email["From"], "*****@*****.**") # make sure we are responding to the right person self.assertEqual(email["To"], some_email) # here we pull out the expected attachment body, attach = email.get_payload() bytes = attach.get_payload() # inflate it back into an xray Dataset fcst = compress.decompress_dataset(base64.b64decode(bytes)) # make sure we're actually compressing the file. self.assertLessEqual(len(bytes), len(zlib.compress(fcst.to_netcdf()))) # make sure the lats are what we expect np.testing.assert_array_almost_equal(fcst["latitude"].values, np.array([-8.53]), 3) self.assertEqual(fcst["latitude"].attrs["units"].lower(), "degrees north") # and the lons np.testing.assert_array_almost_equal(fcst["longitude"].values, np.array([115.54]), 3) self.assertEqual(fcst["longitude"].attrs["units"].lower(), "degrees east") # and the time expected_time = np.linspace(0, 192, 33) expected_time = xray.conventions.decode_cf_datetime(expected_time, fcst["time"].encoding["units"]) np.testing.assert_array_equal(fcst["time"].values, expected_time) self.assertIn("hours", fcst["time"].encoding["units"])
def test_gfs(self): query = {'hours': [0., 3., 9., 24.], 'model': 'gfs', 'domain': {'N': 39., 'S': 35, 'E': -120, 'W': -124}, 'grid_delta': (1., 1.), 'variables': ['wind']} model = grads.GFS() gfs = model.fetch(self.temporary_fcst_path) expected = subset.subset_dataset(gfs, query) compressed = request.process_query(query, url=self.temporary_fcst_path) actual = compress.decompress_dataset(compressed) np.testing.assert_array_almost_equal(actual['x_wind'].values, expected['x_wind'].values, 4) np.testing.assert_array_almost_equal(actual['y_wind'].values, expected['y_wind'].values, 4)