diff --git a/examples/basic.ipynb b/examples/basic.ipynb index 8eceb53..550941d 100644 --- a/examples/basic.ipynb +++ b/examples/basic.ipynb @@ -107,7 +107,9 @@ "id": "tracked-distributor", "metadata": {}, "source": [ - "Now that we have a request, we can submit it to Harmony using the Harmony Client object we created earlier. We'll get back a job id belonging to our Harmony request." + "Now that we have a request, we can submit it to Harmony using the Harmony Client object we created earlier. We'll get back a job id belonging to our Harmony request.\n", + "\n", + "By default the job will have a 'harmony_py' label. This can be disabled by setting the `EXCLUDE_DEFAULT_LABEL` environment variable to \"true\" before making the request." ] }, { diff --git a/examples/intro_tutorial.ipynb b/examples/intro_tutorial.ipynb index be2e21b..6bf9505 100644 --- a/examples/intro_tutorial.ipynb +++ b/examples/intro_tutorial.ipynb @@ -259,7 +259,9 @@ "source": [ "### Submit Request\n", "\n", - "Once you have created your request, you can now submit it to Harmony using the Harmony Client object. A job id is returned, which is a unique identifier that represents your submitted request. " + "Once you have created your request, you can now submit it to Harmony using the Harmony Client object. A job id is returned, which is a unique identifier that represents your submitted request.\n", + "\n", + "By default the job will have a 'harmony_py' label. This can be disabled by setting the `EXCLUDE_DEFAULT_LABEL` environment variable to \"true\" before making the request." ] }, { @@ -625,7 +627,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "harmony-py-notebooks", "language": "python", "name": "python3" }, @@ -639,12 +641,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" - }, - "vscode": { - "interpreter": { - "hash": "d0b323d74a2eccdefaacaf33c041ca03218ebb6c9aa84851ec587afd8a56d428" - } + "version": "3.10.14" } }, "nbformat": 4, diff --git a/harmony/harmony.py b/harmony/harmony.py index e0c2dc6..0f30cd6 100644 --- a/harmony/harmony.py +++ b/harmony/harmony.py @@ -48,6 +48,8 @@ from harmony.config import Config, Environment from harmony import __version__ as harmony_version +DEFAULT_JOB_LABEL = "harmony-py" + progressbar_widgets = [ ' [ Processing: ', progressbar.Percentage(), ' ] ', progressbar.Bar(), @@ -352,7 +354,8 @@ def __init__(self, concatenate: bool = None, skip_preview: bool = None, ignore_errors: bool = None, - grid: str = None): + grid: str = None, + labels: List[str] = None): """Creates a new Request instance from all specified criteria.' """ super().__init__(collection=collection) @@ -377,6 +380,7 @@ def __init__(self, self.skip_preview = skip_preview self.ignore_errors = ignore_errors self.grid = grid + self.labels = labels if self.is_edr_request(): self.variable_name_to_query_param = { @@ -397,7 +401,8 @@ def __init__(self, 'ignore_errors': 'ignoreErrors', 'grid': 'grid', 'extend': 'extend', - 'variables': 'parameter-name' + 'variables': 'parameter-name', + 'labels': 'label', } self.spatial_validations = [ (lambda s: is_wkt_valid(s.wkt), f'WKT {spatial.wkt} is invalid'), @@ -421,7 +426,8 @@ def __init__(self, 'ignore_errors': 'ignoreErrors', 'grid': 'grid', 'extend': 'extend', - 'variables': 'variable' + 'variables': 'variable', + 'labels': 'label', } self.spatial_validations = [ @@ -694,6 +700,10 @@ def _params(self, request: BaseRequest) -> dict: if len(subset) > 0: params['subset'] = subset + if (os.getenv('EXCLUDE_DEFAULT_LABEL') != 'true'): + labels = getattr(params, 'labels', []) + labels.append(DEFAULT_JOB_LABEL) + params['label'] = labels skipped_params = ['shapefile'] query_params = [pv for pv in request.parameter_values() if pv[0] not in skipped_params] @@ -1235,7 +1245,7 @@ def result_urls(self, def _is_staged_result(self, url: str) -> str: """Check if the URL indicates that the data is associated with actual - service ouputs (as opposed to a download link for example). + service outputs (as opposed to a download link for example). Args: url: The location (URL) of the file to be downloaded diff --git a/tests/test_client.py b/tests/test_client.py index cc778ab..3dee896 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -12,7 +12,7 @@ import responses from harmony.harmony import BBox, Client, Collection, LinkType, ProcessingFailedException, Dimension -from harmony.harmony import Request, CapabilitiesRequest +from harmony.harmony import Request, CapabilitiesRequest, DEFAULT_JOB_LABEL @pytest.fixture() @@ -94,6 +94,8 @@ def construct_expected_params(query_string): else: # Add the key-value pair to the dictionary expected_params[key] = value + if (os.getenv('EXCLUDE_DEFAULT_LABEL') != 'true'): + expected_params['label'] = DEFAULT_JOB_LABEL return expected_params def is_expected_url_and_form_encoded_body(harmony_request, http_request): @@ -521,6 +523,52 @@ def test_post_request_has_user_agent_headers(examples_dir): expected_user_agent_header_regex(), user_agent_header ) +@responses.activate +def test_post_request_has_default_label(examples_dir): + collection = Collection('foobar') + request = Request( + collection=collection, + shape=os.path.join(examples_dir, 'asf_example.json'), + spatial=BBox(-107, 40, -105, 42), + ) + responses.add( + responses.POST, + expected_submit_url(collection.id), + status=200, + json=expected_job(collection.id, 'abcd-1234'), + ) + + Client(should_validate_auth=False).submit(request) + form_data_params = parse_multipart_data(responses.calls[0].request) + label = form_data_params['label'] + assert label == DEFAULT_JOB_LABEL + +@responses.activate +def test_post_request_can_skip_default_label(examples_dir): + collection = Collection('foobar') + request = Request( + collection=collection, + shape=os.path.join(examples_dir, 'asf_example.json'), + spatial=BBox(-107, 40, -105, 42), + ) + responses.add( + responses.POST, + expected_submit_url(collection.id), + status=200, + json=expected_job(collection.id, 'abcd-1234'), + ) + + origninal_exclude_label = os.getenv('EXCLUDE_DEFAULT_LABEL') + + os.environ['EXCLUDE_DEFAULT_LABEL'] = 'true' + + Client(should_validate_auth=False).submit(request) + form_data_params = parse_multipart_data(responses.calls[0].request) + assert 'label' not in form_data_params + + os.environ['EXCLUDE_DEFAULT_LABEL'] = origninal_exclude_label or '' + + @pytest.mark.parametrize('param,expected', [ ({'crs': 'epsg:3141'}, 'outputcrs=epsg:3141'), ({'destination_url': 's3://bucket'}, 'destinationUrl=s3://bucket'), @@ -1024,7 +1072,6 @@ def test_download_opendap_file(): match=[responses.matchers.urlencoded_params_matcher({"dap4.ce": "/ds_surf_type[0:1:4]"})]) client = Client(should_validate_auth=False) actual_output = client._download_file(url, overwrite=False) - assert actual_output == expected_filename with open(expected_filename, 'rb') as temp_file: data = temp_file.read() @@ -1537,7 +1584,7 @@ def test_request_as_url(): ) url = Client(should_validate_auth=False).request_as_url(request) - assert url == 'https://harmony.earthdata.nasa.gov/C1940468263-POCLOUD/ogc-api-coverages/1.0.0/collections/parameter_vars/coverage/rangeset?forceAsync=true&subset=lat%2840%3A42%29&subset=lon%28-107%3A-105%29&variable=all' + assert url == f'https://harmony.earthdata.nasa.gov/C1940468263-POCLOUD/ogc-api-coverages/1.0.0/collections/parameter_vars/coverage/rangeset?forceAsync=true&subset=lat%2840%3A42%29&subset=lon%28-107%3A-105%29&label={DEFAULT_JOB_LABEL}&variable=all' def test_request_with_shapefile_as_url(examples_dir): collection = Collection(id='C1940468263-POCLOUD')