From cdadbfe265a0f59bd218f5bcff5bf6c61b318dda Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Fri, 26 Jan 2018 15:21:10 -0800 Subject: [PATCH 1/7] Add download model functionality --- cesium_app/app_server.py | 3 ++- cesium_app/handlers/model.py | 22 ++++++++++++++++------ static/css/base.css | 5 ++++- static/js/components/Download.jsx | 28 ++++++++++++++++++++++++++++ static/js/components/Models.jsx | 12 ++++++++---- 5 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 static/js/components/Download.jsx diff --git a/cesium_app/app_server.py b/cesium_app/app_server.py index 6af65d9..b6419ff 100644 --- a/cesium_app/app_server.py +++ b/cesium_app/app_server.py @@ -57,7 +57,8 @@ def make_app(cfg, baselayer_handlers, baselayer_settings): (r'/project(/.*)?', ProjectHandler), (r'/dataset(/.*)?', DatasetHandler), (r'/features(/.*)?', FeatureHandler), - (r'/models(/.*)?', ModelHandler), + (r'/models(/[0-9]+)?', ModelHandler), + (r'/models/([0-9]+)/(download)', ModelHandler), (r'/predictions(/[0-9]+)?', PredictionHandler), (r'/predictions/([0-9]+)/(download)', PredictionHandler), (r'/predict_raw_data', PredictRawDataHandler), diff --git a/cesium_app/handlers/model.py b/cesium_app/handlers/model.py index 5aa4fe1..83c24bf 100644 --- a/cesium_app/handlers/model.py +++ b/cesium_app/handlers/model.py @@ -72,14 +72,24 @@ def _build_model_compute_statistics(fset_path, model_type, model_params, class ModelHandler(BaseHandler): @auth_or_token - def get(self, model_id=None): - if model_id is not None: - model_info = Model.get_if_owned_by(model_id, self.current_user) + def get(self, model_id=None, action=None): + if action == 'download': + model_path = Model.get_if_owned_by(model_id, + self.current_user).file_uri + with open(model_path, 'rb') as f: + model_data = f.read() + self.set_header("Content-Type", "application/octet-stream") + self.set_header("Content-Disposition", "attachment; " + "filename=cesium_model__joblib.pkl") + self.write(model_data) else: - model_info = [model for p in self.current_user.projects - for model in p.models] + if model_id is not None: + model_info = Model.get_if_owned_by(model_id, self.current_user) + else: + model_info = [model for p in self.current_user.projects + for model in p.models] - return self.success(model_info) + return self.success(model_info) @auth_or_token async def _await_model_statistics(self, model_stats_future, model): diff --git a/static/css/base.css b/static/css/base.css index c85a77b..ec3f502 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -33,4 +33,7 @@ body { .loginBox .logo { float: left; padding: 1em; -} \ No newline at end of file +} +a:hover { + cursor:pointer; +} diff --git a/static/js/components/Download.jsx b/static/js/components/Download.jsx new file mode 100644 index 0000000..8cc3990 --- /dev/null +++ b/static/js/components/Download.jsx @@ -0,0 +1,28 @@ +import React from 'react'; + +const Download = (props) => { + const style = { + display: 'inline-block' + }; + return ( + { + e.stopPropagation(); + } + } + > + Download + + ); +}; +Download.propTypes = { + ID: React.PropTypes.oneOfType([ + React.PropTypes.number, + React.PropTypes.string]).isRequired, + url: React.PropTypes.string.isRequired +}; + +export default Download; diff --git a/static/js/components/Models.jsx b/static/js/components/Models.jsx index e76e47e..3c994b0 100644 --- a/static/js/components/Models.jsx +++ b/static/js/components/Models.jsx @@ -10,6 +10,7 @@ import * as Validate from '../validate'; import * as Action from '../actions'; import Expand from './Expand'; import Delete from './Delete'; +import Download from './Download'; import { $try, reformatDatetime } from '../utils'; import FoldableRow from './FoldableRow'; @@ -237,7 +238,11 @@ export let ModelTable = props => ( {model.name} {reformatDatetime(model.created_at)} {status} - + + +    + + {foldedContent} @@ -265,11 +270,10 @@ const mtMapStateToProps = (state, ownProps) => ( ModelTable = connect(mtMapStateToProps)(ModelTable); -const dmMapDispatchToProps = dispatch => ( +const deleteMapDispatchToProps = dispatch => ( { delete: id => dispatch(Action.deleteModel(id)) } ); - -const DeleteModel = connect(null, dmMapDispatchToProps)(Delete); +const DeleteModelButton = connect(null, deleteMapDispatchToProps)(Delete); export default ModelsTab; From 01fe865cb7a8aa3109a6b0d5ef0733da22d59def Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Fri, 26 Jan 2018 15:43:49 -0800 Subject: [PATCH 2/7] Add download featureset functionality Improve downloaded featureset formatting --- cesium_app/app_server.py | 3 ++- cesium_app/handlers/feature.py | 32 ++++++++++++++++++++++--------- static/js/components/Features.jsx | 8 ++++++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cesium_app/app_server.py b/cesium_app/app_server.py index b6419ff..8b4b5c4 100644 --- a/cesium_app/app_server.py +++ b/cesium_app/app_server.py @@ -56,7 +56,8 @@ def make_app(cfg, baselayer_handlers, baselayer_settings): handlers = baselayer_handlers + [ (r'/project(/.*)?', ProjectHandler), (r'/dataset(/.*)?', DatasetHandler), - (r'/features(/.*)?', FeatureHandler), + (r'/features(/[0-9]+)?', FeatureHandler), + (r'/features/([0-9]+)/(download)', FeatureHandler), (r'/models(/[0-9]+)?', ModelHandler), (r'/models/([0-9]+)/(download)', ModelHandler), (r'/predictions(/[0-9]+)?', PredictionHandler), diff --git a/cesium_app/handlers/feature.py b/cesium_app/handlers/feature.py index e2000e2..f838516 100644 --- a/cesium_app/handlers/feature.py +++ b/cesium_app/handlers/feature.py @@ -13,20 +13,34 @@ from os.path import join as pjoin import uuid import datetime +import pandas as pd class FeatureHandler(BaseHandler): @auth_or_token - def get(self, featureset_id=None): - if featureset_id is not None: - featureset_info = Featureset.get_if_owned_by(featureset_id, - self.current_user) + def get(self, featureset_id=None, action=None): + if action == 'download': + fset_path = Featureset.get_if_owned_by(featureset_id, + self.current_user).file_uri + print(fset_path) + fset, data = featurize.load_featureset(fset_path) + fset.index.name = 'ts_name' + fset.columns = fset.columns.get_level_values(0) + fset.columns.name = None + self.set_header("Content-Type", 'text/csv; charset="utf-8"') + self.set_header("Content-Disposition", "attachment; " + "filename=cesium_featureset.csv") + self.write(fset.to_csv(index=True)) else: - featureset_info = [f for p in self.current_user.projects - for f in p.featuresets] - featureset_info.sort(key=lambda f: f.created_at, reverse=True) - - self.success(featureset_info) + if featureset_id is not None: + featureset_info = Featureset.get_if_owned_by(featureset_id, + self.current_user) + else: + featureset_info = [f for p in self.current_user.projects + for f in p.featuresets] + featureset_info.sort(key=lambda f: f.created_at, reverse=True) + + self.success(featureset_info) @auth_or_token async def _await_featurization(self, future, fset): diff --git a/static/js/components/Features.jsx b/static/js/components/Features.jsx index 4e61ce3..08a2353 100644 --- a/static/js/components/Features.jsx +++ b/static/js/components/Features.jsx @@ -13,6 +13,7 @@ import Plot from './Plot'; import FoldableRow from './FoldableRow'; import { reformatDatetime, contains } from '../utils'; import Delete from './Delete'; +import Download from './Download'; const { Tab, Tabs, TabList, TabPanel } = { ...ReactTabs }; @@ -257,7 +258,11 @@ export let FeatureTable = props => ( {featureset.name} {reformatDatetime(featureset.created_at)} {status} - + + +    + + {foldedContent} @@ -290,7 +295,6 @@ FeatureTable = connect(ftMapStateToProps)(FeatureTable); const mapDispatchToProps = dispatch => ( { delete: id => dispatch(Action.deleteFeatureset(id)) } ); - const DeleteFeatureset = connect(null, mapDispatchToProps)(Delete); export default FeaturesTab; From d4b84c270ac2fba1644ac74055d0378dd15b9ace Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Tue, 6 Feb 2018 11:27:39 -0800 Subject: [PATCH 3/7] Only render download link when computation is complete Add featureset/model/prediction name & timestamp to default download file name Add project name to default download file names --- cesium_app/handlers/feature.py | 11 +++++++---- cesium_app/handlers/model.py | 10 ++++++---- cesium_app/handlers/prediction.py | 11 +++++++---- static/js/components/Features.jsx | 5 ++++- static/js/components/Models.jsx | 5 ++++- static/js/components/Predictions.jsx | 5 ++++- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/cesium_app/handlers/feature.py b/cesium_app/handlers/feature.py index f838516..caa4b8a 100644 --- a/cesium_app/handlers/feature.py +++ b/cesium_app/handlers/feature.py @@ -20,16 +20,19 @@ class FeatureHandler(BaseHandler): @auth_or_token def get(self, featureset_id=None, action=None): if action == 'download': - fset_path = Featureset.get_if_owned_by(featureset_id, - self.current_user).file_uri + featureset = Featureset.get_if_owned_by(featureset_id, + self.current_user) + fset_path = featureset.file_uri print(fset_path) fset, data = featurize.load_featureset(fset_path) fset.index.name = 'ts_name' fset.columns = fset.columns.get_level_values(0) fset.columns.name = None self.set_header("Content-Type", 'text/csv; charset="utf-8"') - self.set_header("Content-Disposition", "attachment; " - "filename=cesium_featureset.csv") + self.set_header( + "Content-Disposition", "attachment; " + f"filename=cesium_featureset_{featureset.project.name}" + f"_{featureset.name}_{featureset.finished}.csv") self.write(fset.to_csv(index=True)) else: if featureset_id is not None: diff --git a/cesium_app/handlers/model.py b/cesium_app/handlers/model.py index 83c24bf..218f4fd 100644 --- a/cesium_app/handlers/model.py +++ b/cesium_app/handlers/model.py @@ -74,13 +74,15 @@ class ModelHandler(BaseHandler): @auth_or_token def get(self, model_id=None, action=None): if action == 'download': - model_path = Model.get_if_owned_by(model_id, - self.current_user).file_uri + model = Model.get_if_owned_by(model_id, self.current_user) + model_path = model.file_uri with open(model_path, 'rb') as f: model_data = f.read() self.set_header("Content-Type", "application/octet-stream") - self.set_header("Content-Disposition", "attachment; " - "filename=cesium_model__joblib.pkl") + self.set_header( + "Content-Disposition", "attachment; " + f"filename=cesium_model__joblib_{model.project.name}" + f"_{model.name}_{model.finished}.pkl") self.write(model_data) else: if model_id is not None: diff --git a/cesium_app/handlers/prediction.py b/cesium_app/handlers/prediction.py index 406f102..f249470 100644 --- a/cesium_app/handlers/prediction.py +++ b/cesium_app/handlers/prediction.py @@ -121,8 +121,8 @@ async def post(self): @auth_or_token def get(self, prediction_id=None, action=None): if action == 'download': - pred_path = Prediction.get_if_owned_by(prediction_id, - self.current_user).file_uri + prediction = Prediction.get_if_owned_by(prediction_id, self.current_user) + pred_path = prediction.file_uri fset, data = featurize.load_featureset(pred_path) result = pd.DataFrame(({'label': data['labels']} if len(data['labels']) > 0 else None), @@ -133,8 +133,11 @@ def get(self, prediction_id=None, action=None): result['prediction'] = data['preds'] result.index.name = 'ts_name' self.set_header("Content-Type", 'text/csv; charset="utf-8"') - self.set_header("Content-Disposition", "attachment; " - "filename=cesium_prediction_results.csv") + self.set_header( + "Content-Disposition", "attachment; " + f"filename=cesium_prediction_results_{prediction.project.name}" + f"_{prediction.dataset.name}" + f"_{prediction.model.name}_{prediction.finished}.csv") self.write(result.to_csv(index=True)) else: if prediction_id is None: diff --git a/static/js/components/Features.jsx b/static/js/components/Features.jsx index 08a2353..c574f8e 100644 --- a/static/js/components/Features.jsx +++ b/static/js/components/Features.jsx @@ -259,7 +259,10 @@ export let FeatureTable = props => ( {reformatDatetime(featureset.created_at)} {status} - + { + done && + + }    diff --git a/static/js/components/Models.jsx b/static/js/components/Models.jsx index 3c994b0..8f3b6aa 100644 --- a/static/js/components/Models.jsx +++ b/static/js/components/Models.jsx @@ -239,7 +239,10 @@ export let ModelTable = props => ( {reformatDatetime(model.created_at)} {status} - + { + done && + + }    diff --git a/static/js/components/Predictions.jsx b/static/js/components/Predictions.jsx index dd1245d..0071a83 100644 --- a/static/js/components/Predictions.jsx +++ b/static/js/components/Predictions.jsx @@ -135,7 +135,10 @@ let PredictionsTable = props => ( {reformatDatetime(prediction.created_at)} {status} - + { + done && + + }    From 3be4839b7be997b0fca5cc8a7299493a07126b36 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Tue, 6 Feb 2018 13:15:28 -0800 Subject: [PATCH 4/7] Add sklearn and joblib versions to downloaded model file names Remove debug print statement --- cesium_app/handlers/feature.py | 3 +-- cesium_app/handlers/model.py | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cesium_app/handlers/feature.py b/cesium_app/handlers/feature.py index caa4b8a..ea4b846 100644 --- a/cesium_app/handlers/feature.py +++ b/cesium_app/handlers/feature.py @@ -23,10 +23,9 @@ def get(self, featureset_id=None, action=None): featureset = Featureset.get_if_owned_by(featureset_id, self.current_user) fset_path = featureset.file_uri - print(fset_path) fset, data = featurize.load_featureset(fset_path) fset.index.name = 'ts_name' - fset.columns = fset.columns.get_level_values(0) + fset.columns = fset.columns.droplevel('channel') fset.columns.name = None self.set_header("Content-Type", 'text/csv; charset="utf-8"') self.set_header( diff --git a/cesium_app/handlers/model.py b/cesium_app/handlers/model.py index 218f4fd..c8fbfd7 100644 --- a/cesium_app/handlers/model.py +++ b/cesium_app/handlers/model.py @@ -13,6 +13,7 @@ import uuid import datetime +import sklearn from sklearn.model_selection import GridSearchCV import joblib @@ -81,8 +82,10 @@ def get(self, model_id=None, action=None): self.set_header("Content-Type", "application/octet-stream") self.set_header( "Content-Disposition", "attachment; " - f"filename=cesium_model__joblib_{model.project.name}" - f"_{model.name}_{model.finished}.pkl") + f"filename=cesium_model_{model.project.name}" + f"_{model.name}_{str(model.finished).replace(' ', 'T')}" + f"_joblib_v{joblib.__version__}" + f"_sklearn_v{sklearn.__version__}.pkl") self.write(model_data) else: if model_id is not None: From f04a2a9b1ebf2af0d5ef576d4c7ea6a95452827e Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 15 Feb 2018 12:05:33 -0800 Subject: [PATCH 5/7] Update feature downloading for multi-channel; include labels De-lint --- cesium_app/handlers/feature.py | 5 ++--- static/js/components/Download.jsx | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cesium_app/handlers/feature.py b/cesium_app/handlers/feature.py index ea4b846..8d84511 100644 --- a/cesium_app/handlers/feature.py +++ b/cesium_app/handlers/feature.py @@ -24,9 +24,8 @@ def get(self, featureset_id=None, action=None): self.current_user) fset_path = featureset.file_uri fset, data = featurize.load_featureset(fset_path) - fset.index.name = 'ts_name' - fset.columns = fset.columns.droplevel('channel') - fset.columns.name = None + if 'labels' in data: + fset['labels'] = data['labels'] self.set_header("Content-Type", 'text/csv; charset="utf-8"') self.set_header( "Content-Disposition", "attachment; " diff --git a/static/js/components/Download.jsx b/static/js/components/Download.jsx index 8cc3990..526f3ab 100644 --- a/static/js/components/Download.jsx +++ b/static/js/components/Download.jsx @@ -1,4 +1,6 @@ import React from 'react'; +import PropTypes from 'prop-types'; + const Download = (props) => { const style = { @@ -19,10 +21,7 @@ const Download = (props) => { ); }; Download.propTypes = { - ID: React.PropTypes.oneOfType([ - React.PropTypes.number, - React.PropTypes.string]).isRequired, - url: React.PropTypes.string.isRequired + url: PropTypes.string.isRequired }; export default Download; From f53f6da4225d6908173b8c1ef1989d7c7bb1aa9d Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Fri, 4 May 2018 12:37:51 -0700 Subject: [PATCH 6/7] Fix prediction results download path bug in tests --- cesium_app/tests/frontend/test_predict.py | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/cesium_app/tests/frontend/test_predict.py b/cesium_app/tests/frontend/test_predict.py index 2e9e2dd..1e32447 100644 --- a/cesium_app/tests/frontend/test_predict.py +++ b/cesium_app/tests/frontend/test_predict.py @@ -10,6 +10,7 @@ import pandas as pd import json import subprocess +import glob from cesium_app.model_util import create_token_user @@ -154,10 +155,11 @@ def test_download_prediction_csv_class(driver, project, dataset, featureset, model, prediction): driver.get('/') _click_download(project.id, driver) - assert os.path.exists('/tmp/cesium_prediction_results.csv') + matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + assert len(matching_downloads_paths) == 1 try: npt.assert_equal( - np.genfromtxt('/tmp/cesium_prediction_results.csv', dtype='str'), + np.genfromtxt(matching_downloads_paths[0], dtype='str'), ['ts_name,label,prediction', '0,Mira,Mira', '1,Classical_Cepheid,Classical_Cepheid', @@ -165,30 +167,32 @@ def test_download_prediction_csv_class(driver, project, dataset, featureset, '3,Classical_Cepheid,Classical_Cepheid', '4,Mira,Mira']) finally: - os.remove('/tmp/cesium_prediction_results.csv') + os.remove(matching_downloads_paths[0]) @pytest.mark.parametrize('model__type', ['LinearSGDClassifier']) def test_download_prediction_csv_class_unlabeled(driver, project, unlabeled_prediction): driver.get('/') _click_download(project.id, driver) - assert os.path.exists('/tmp/cesium_prediction_results.csv') + matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + assert len(matching_downloads_paths) == 1 try: - result = np.genfromtxt('/tmp/cesium_prediction_results.csv', dtype='str') + result = np.genfromtxt(matching_downloads_paths[0], dtype='str') assert result[0] == 'ts_name,prediction' assert all([el[0].isdigit() and el[1] == ',' and el[2:] in ['Mira', 'Classical_Cepheid'] for el in result[1:]]) finally: - os.remove('/tmp/cesium_prediction_results.csv') + os.remove(matching_downloads_paths[0]) def test_download_prediction_csv_class_prob(driver, project, dataset, featureset, model, prediction): driver.get('/') _click_download(project.id, driver) - assert os.path.exists('/tmp/cesium_prediction_results.csv') + matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + assert len(matching_downloads_paths) == 1 try: - result = pd.read_csv('/tmp/cesium_prediction_results.csv') + result = pd.read_csv(matching_downloads_paths[0]) npt.assert_array_equal(result.ts_name, np.arange(5)) npt.assert_array_equal(result.label, ['Mira', 'Classical_Cepheid', 'Mira', 'Classical_Cepheid', @@ -198,16 +202,17 @@ def test_download_prediction_csv_class_prob(driver, project, dataset, [1, 0, 1, 0, 1]) assert (pred_probs.values >= 0.0).all() finally: - os.remove('/tmp/cesium_prediction_results.csv') + os.remove(matching_downloads_paths[0]) @pytest.mark.parametrize('featureset__name, model__type', [('regr', 'LinearRegressor')]) def test_download_prediction_csv_regr(driver, project, dataset, featureset, model, prediction): driver.get('/') _click_download(project.id, driver) - assert os.path.exists('/tmp/cesium_prediction_results.csv') + matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + assert len(matching_downloads_paths) == 1 try: - results = np.genfromtxt('/tmp/cesium_prediction_results.csv', + results = np.genfromtxt(matching_downloads_paths[0], dtype='str', delimiter=',') npt.assert_equal(results[0], ['ts_name', 'label', 'prediction']) @@ -219,7 +224,7 @@ def test_download_prediction_csv_regr(driver, project, dataset, featureset, mode [3, 2.2, 2.2], [4, 3.1, 3.1]]) finally: - os.remove('/tmp/cesium_prediction_results.csv') + os.remove(matching_downloads_paths[0]) def test_predict_specific_ts_name(driver, project, dataset, featureset, model): From 4f00d1a5b0bf8877e4b2c4394f89f082492392b8 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Fri, 4 May 2018 13:20:55 -0700 Subject: [PATCH 7/7] Use downloads path from config in prediction tests --- cesium_app/tests/frontend/test_predict.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cesium_app/tests/frontend/test_predict.py b/cesium_app/tests/frontend/test_predict.py index 1e32447..287189f 100644 --- a/cesium_app/tests/frontend/test_predict.py +++ b/cesium_app/tests/frontend/test_predict.py @@ -12,6 +12,10 @@ import subprocess import glob from cesium_app.model_util import create_token_user +from baselayer.app.config import load_config + + +cfg = load_config() def _add_prediction(proj_id, driver): @@ -155,7 +159,8 @@ def test_download_prediction_csv_class(driver, project, dataset, featureset, model, prediction): driver.get('/') _click_download(project.id, driver) - matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + matching_downloads_paths = glob.glob(f'{cfg["paths:downloads_folder"]}/' + 'cesium_prediction_results*.csv') assert len(matching_downloads_paths) == 1 try: npt.assert_equal( @@ -174,7 +179,8 @@ def test_download_prediction_csv_class(driver, project, dataset, featureset, def test_download_prediction_csv_class_unlabeled(driver, project, unlabeled_prediction): driver.get('/') _click_download(project.id, driver) - matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + matching_downloads_paths = glob.glob(f'{cfg["paths:downloads_folder"]}/' + 'cesium_prediction_results*.csv') assert len(matching_downloads_paths) == 1 try: result = np.genfromtxt(matching_downloads_paths[0], dtype='str') @@ -189,7 +195,8 @@ def test_download_prediction_csv_class_prob(driver, project, dataset, featureset, model, prediction): driver.get('/') _click_download(project.id, driver) - matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + matching_downloads_paths = glob.glob(f'{cfg["paths:downloads_folder"]}/' + 'cesium_prediction_results*.csv') assert len(matching_downloads_paths) == 1 try: result = pd.read_csv(matching_downloads_paths[0]) @@ -206,10 +213,12 @@ def test_download_prediction_csv_class_prob(driver, project, dataset, @pytest.mark.parametrize('featureset__name, model__type', [('regr', 'LinearRegressor')]) -def test_download_prediction_csv_regr(driver, project, dataset, featureset, model, prediction): +def test_download_prediction_csv_regr(driver, project, dataset, featureset, + model, prediction): driver.get('/') _click_download(project.id, driver) - matching_downloads_paths = glob.glob('/tmp/cesium_prediction_results*.csv') + matching_downloads_paths = glob.glob(f'{cfg["paths:downloads_folder"]}/' + 'cesium_prediction_results*.csv') assert len(matching_downloads_paths) == 1 try: results = np.genfromtxt(matching_downloads_paths[0],