From dd50bc0c6678bd6685b97d1b06903850e2410222 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 16 Aug 2023 12:56:44 -0700 Subject: [PATCH 01/20] updated test notebook --- CHANGELOG.md | 2 + .../harmony_concise_api_test.ipynb | 225 +++++++++++++----- 2 files changed, 171 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b58acbd9..9d078b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - PODAAC-5424 - Update concise test in uat to test only POCLOUD collections ### Changed + - Issue-68 + - Updated jupyter notebook ### Deprecated ### Removed ### Fixed diff --git a/jupyter_notebooks/harmony_concise_api_test.ipynb b/jupyter_notebooks/harmony_concise_api_test.ipynb index 4ff691cf..f9a2de83 100644 --- a/jupyter_notebooks/harmony_concise_api_test.ipynb +++ b/jupyter_notebooks/harmony_concise_api_test.ipynb @@ -36,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "id": "3c95100d", "metadata": {}, "outputs": [], @@ -58,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "id": "0c9b70ea", "metadata": { "tags": [ @@ -67,7 +67,7 @@ }, "outputs": [], "source": [ - "collection_id = 'C1940473819-POCLOUD'\n", + "collection_id = 'C2205121485-POCLOUD'\n", "max_results = 2\n", "venue = \"OPS\"" ] @@ -82,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "id": "5ccd035f", "metadata": {}, "outputs": [], @@ -109,10 +109,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "b11675ac", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "if venue == \"UAT\":\n", " harmony_client = Client(auth=(username, password), env=Environment.UAT)\n", @@ -144,10 +155,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "id": "36ce8016", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "curl -X GET -H 'Accept: */*' -H 'Accept-Encoding: gzip, deflate' -H 'Authorization: *****' -H 'Connection: keep-alive' -H 'Cookie: urs_user_already_logged=yes; state=s%3A1c51c3d53a38e6af99f0366288cd18bf.p404RcPg%2BvidV0pdWb9gqeHVcz4Hld5s5Q5PexRzbXs; token=*****; _urs-gui_session=a61daf78fa6987f16d1ffc770d775b69' -H 'User-Agent: CPython/3.8.12 harmony-py/0.4.2 Darwin/22.6.0 python-requests/2.27.1' 'https://harmony.earthdata.nasa.gov/C2205121485-POCLOUD/ogc-api-coverages/1.0.0/collections/all/coverage/rangeset?forceAsync=true&format=application%2Fx-netcdf4&maxResults=2&concatenate=true&skipPreview=true'\n" + ] + } + ], "source": [ "print(harmony_client.request_as_curl(request))\n", "\n", @@ -166,10 +185,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "id": "0d13a446", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "0790ef60-4c3b-4189-b9ac-d9dad2aafafe\n", + "{'status': 'running', 'message': 'CMR query identified 1308 granules, but the request has been limited to process only the first 2 granules because you requested 2 maxResults.', 'progress': 0, 'created_at': datetime.datetime(2023, 8, 16, 17, 32, 22, 669000, tzinfo=tzutc()), 'updated_at': datetime.datetime(2023, 8, 16, 17, 32, 22, 669000, tzinfo=tzutc()), 'created_at_local': '2023-08-16T10:32:22-07:00', 'updated_at_local': '2023-08-16T10:32:22-07:00', 'data_expiration': datetime.datetime(2023, 9, 15, 17, 32, 22, 669000, tzinfo=tzutc()), 'data_expiration_local': '2023-09-15T10:32:22-07:00', 'request': 'https://harmony.earthdata.nasa.gov/C2205121485-POCLOUD/ogc-api-coverages/1.0.0/collections/all/coverage/rangeset?forceAsync=true&format=application%2Fx-netcdf4&maxResults=2&concatenate=true&skipPreview=true', 'num_input_granules': 2}\n", + "\n", + "Waiting for the job to finish\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " [ Processing: 100% ] |###################################################| [|]\n" + ] + } + ], "source": [ "print(f'\\n{job1_id}')\n", "\n", @@ -189,10 +227,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "id": "168e7b11", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Downloading results:\n", + "\n", + "C2205121485-POCLOUD_merged.nc4\n", + "\n", + "Done downloading.\n" + ] + } + ], "source": [ "print('\\nDownloading results:')\n", "filename = None\n", @@ -217,7 +268,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "id": "04db3295", "metadata": { "scrolled": true @@ -226,30 +277,19 @@ "source": [ "#some collections time variabe has a time dimension which can cause an exception when we concatenate and makes time two dimension\n", "dt = nc.Dataset(filename,'r')\n", - "group = list(dt.groups)\n", + "groups = list(dt.groups)\n", "dt.close()\n", - "\n", - "if len(group) == 0:\n", - " group = None\n", - "else:\n", - " group = group[0]\n", " \n", "drop_variables = [\n", " 'time',\n", " 'sample',\n", " 'meas_ind',\n", " 'wvf_ind',\n", - " 'ddm'\n", + " 'ddm',\n", + " 'averaged_l1'\n", "]\n", - " \n", - "try:\n", - " ds = xr.open_dataset(filename, group=group, decode_times=False)\n", - "except xr.core.variable.MissingDimensionsError:\n", - " ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables)\n", - "\n", - "print(list(ds.variables))\n", - " \n", - "assert len(ds.coords['subset_index']) == max_results" + "if len(groups) == 0:\n", + " groups = [None]" ] }, { @@ -262,36 +302,96 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "id": "a733274d", - "metadata": {}, - "outputs": [], + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['subset_files', 'ddm_source', 'spacecraft_id', 'spacecraft_num', 'prn_code', 'sv_num', 'antenna', 'sample_time', 'lat', 'lon', 'sc_lat', 'sc_lon', 'sc_alt', 'wind_speed', 'fds_nbrcs_wind_speed', 'fds_les_wind_speed', 'wind_speed_uncertainty', 'azimuth_angle', 'sc_roll', 'commanded_sc_roll', 'mean_square_slope', 'mean_square_slope_uncertainty', 'incidence_angle', 'nbrcs_mean', 'les_mean', 'range_corr_gain', 'fresnel_coeff', 'merra2_wind_speed', 'num_ddms_utilized', 'sample_flags', 'fds_sample_flags', 'sum_neg_brcs_value_used_for_nbrcs_flags', 'ddm_obs_utilized_flag', 'ddm_num_averaged_l1', 'ddm_channel', 'ddm_les', 'ddm_nbrcs', 'ddm_sample_index', 'ddm_averaged_l1_utilized_flag']\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ - "variable = None\n", - "variables = list(ds.variables)\n", + "for group in groups:\n", + "\n", + " try:\n", + " ds = xr.open_dataset(filename, group=group, decode_times=False)\n", + " except xr.core.variable.MissingDimensionsError:\n", + " ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables)\n", "\n", - "for v in variables:\n", - " if v not in ['subset_files', 'lat', 'lon', 'latitude', 'longitude', 'beam_clat', 'beam_clon']:\n", - " variable = v\n", - " break;\n", + " assert len(ds.coords['subset_index']) == max_results\n", "\n", - "if \"lon\" in variables:\n", - " x = \"lon\"\n", - "elif \"longitude\" in variables:\n", - " x = \"longitude\"\n", - "elif \"beam_clon\" in variables:\n", - " x = \"beam_clon\"\n", - "elif \"sp_lon\" in variables:\n", - " x = \"sp_lon\"\n", + " variable = None\n", + " variables = list(ds.variables)\n", + " print(variables)\n", " \n", - "if \"lat\" in variables:\n", - " y = \"lon\"\n", - "elif \"latitude\" in variables:\n", - " y = \"longitude\"\n", - "elif \"beam_clat\" in variables:\n", - " y = \"beam_clat\"\n", - "elif \"sp_lat\" in variables:\n", - " y = \"sp_lat\"\n", + " for v in variables:\n", + " if v not in ['subset_files', 'lat', 'lon', 'latitude', 'longitude', 'beam_clat', 'beam_clon']:\n", + " variable = v\n", + " break;\n", + "\n", + " if \"lon\" in variables:\n", + " x = \"lon\"\n", + " elif \"longitude\" in variables:\n", + " x = \"longitude\"\n", + " elif \"beam_clon\" in variables:\n", + " x = \"beam_clon\"\n", + " elif \"sp_lon\" in variables:\n", + " x = \"sp_lon\"\n", + " elif \"cellon\" in variables:\n", + " x = \"cellon\"\n", + " else:\n", + " x = None\n", + "\n", + " if \"lat\" in variables:\n", + " y = \"lon\"\n", + " elif \"latitude\" in variables:\n", + " y = \"longitude\"\n", + " elif \"beam_clat\" in variables:\n", + " y = \"beam_clat\"\n", + " elif \"sp_lat\" in variables:\n", + " y = \"sp_lat\"\n", + " elif \"cellat\" in variables:\n", + " y = \"cellat\"\n", + " else:\n", + " y = None\n", + " \n", + " if y is not None and x is not None:\n", + " break\n", + " \n", + " ds.close()\n", + "\n", + "if x is None or y is None:\n", + " raise Exception(\"Lon and Lat variables are not found\")\n", " \n", "for index in range(0, max_results):\n", " \n", @@ -321,10 +421,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "id": "748cdb33", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "ds.plot.scatter(\n", " y=y,\n", From 4d77f6ba5e530d7047b86cafb22dc6ea1b23214f Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 22 Aug 2023 16:14:55 -0700 Subject: [PATCH 02/20] update notebook test to not use notebooks --- notebook_test.py | 168 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 159 insertions(+), 9 deletions(-) diff --git a/notebook_test.py b/notebook_test.py index 9ae6e798..3aaa7fb9 100644 --- a/notebook_test.py +++ b/notebook_test.py @@ -1,3 +1,8 @@ +import os +import matplotlib.pyplot as plt +import netCDF4 as nc +import xarray as xr +from harmony import BBox, Client, Collection, Request, Environment import papermill as pm import argparse @@ -7,6 +12,7 @@ from utils.enums import Venue import itertools + def parse_args(): """ Parses the program arguments @@ -28,7 +34,7 @@ def parse_args(): parser.add_argument('-n', '--notebook', help='Notebook to run', - required=True, + required=False, metavar='') parser.add_argument('-i', '--input_file', @@ -44,6 +50,146 @@ def parse_args(): args = parser.parse_args() return args + +def get_username_and_password(venue): + if venue == "UAT": + return os.environ.get("UAT_USERNAME"), os.environ.get("UAT_PASSWORD") + elif venue == "OPS": + return os.environ.get('OPS_USERNAME'), os.environ.get('OPS_PASSWORD') + else: + raise ValueError("Invalid venue") + + +def get_x_y_variables(variables): + x_var_candidates = ["lon", "longitude", "beam_clon", "sp_lon", "cellon"] + y_var_candidates = ["lat", "latitude", "beam_clat", "sp_lat", "cellat"] + + x_var, y_var = None, None + for var in variables: + if x_var is None and var in x_var_candidates: + x_var = var + if y_var is None and var in y_var_candidates: + y_var = var + if x_var and y_var: + break + + return x_var, y_var + + +def test(collection_id, venue): + + max_results = 2 + + username, password = get_username_and_password(venue) + environment = Environment.UAT if venue == "UAT" else Environment.PROD + harmony_client = Client(auth=(username, password), env=environment) + + collection = Collection(id=collection_id) + + request = Request( + collection=collection, + concatenate=True, + max_results=max_results, + skip_preview=True, + format="application/x-netcdf4", + ) + + request.is_valid() + + print(harmony_client.request_as_curl(request)) + + job1_id = harmony_client.submit(request) + + print(f'\n{job1_id}') + + print(harmony_client.status(job1_id)) + + print('\nWaiting for the job to finish') + + harmony_client.wait_for_processing(job1_id, show_progress=True) + results = harmony_client.result_json(job1_id) + + print('\nDownloading results:') + #filename = harmony_client.download_all(job1_id, overwrite=True)[0].result() + + futures = harmony_client.download_all(job1_id) + file_names = [f.result() for f in futures] + print('\nDone downloading.') + + filename = file_names[0] + # Handle time dimension and variables dropping + dt = nc.Dataset(filename, 'r') + groups = list(dt.groups) + dt.close() + + drop_variables = [ + 'time', + 'sample', + 'meas_ind', + 'wvf_ind', + 'ddm', + 'averaged_l1' + ] + if not groups: + groups = [None] + + for group in groups: + try: + ds = xr.open_dataset(filename, group=group, decode_times=False) + except xr.core.variable.MissingDimensionsError: + ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) + + assert len(ds.coords['subset_index']) == max_results + variables = list(ds.variables) + x_var, y_var = get_x_y_variables(variables) + + for v in variables: + if v not in ['subset_files', 'lat', 'lon', 'latitude', 'longitude', 'beam_clat', 'beam_clon']: + variable = v + break + + if x_var is not None and y_var is not None: + break + + ds.close() + + if x_var is None or y_var is None: + raise Exception("Lon and Lat variables are not found") + + for index in range(0, max_results): + ax = ds.isel(subset_index=index).plot.scatter( + y=y_var, + x=x_var, + hue=variable, + s=1, + levels=9, + cmap="jet", + aspect=2.5, + size=9 + ) + plt.xlim(0., 360.) + plt.ylim(-90., 90.) + plt.show(block=False) + plt.close(ax.figure) + + ax = ds.plot.scatter( + y=y_var, + x=x_var, + hue=variable, + s=1, + levels=9, + cmap="jet", + aspect=2.5, + size=9 + ) + plt.xlim(0., 360.) + plt.ylim(-90., 90.) + plt.show(block=False) + plt.close(ax.figure) + + ds.close() + + def run(): """ Run from command line. @@ -56,10 +202,10 @@ def run(): environment = _args.env notebook = _args.notebook inputFile = _args.input_file - output_location = _args.output_path if _args.output_path else '.' + output_location = _args.output_path if _args.output_path else '.' - notebook_path = path.realpath(path.dirname(notebook)) - notebook_name = path.basename(notebook) + #notebook_path = path.realpath(path.dirname(notebook)) + #notebook_name = path.basename(notebook) success = [] fails = [] @@ -67,6 +213,7 @@ def run(): if path.exists(inputFile): venue = Venue.from_str(environment) collections = FileHandler.get_file_content_list_per_line(inputFile) + print(collections) # limit number of collections tested to 1 for collection in itertools.islice(collections, 1): if "POCLOUD" not in collection and venue == "uat": @@ -74,11 +221,13 @@ def run(): try: print(collection) - pm.execute_notebook( - notebook, - f"{notebook_path}/output/{collection}_{environment}_output_{notebook_name}", - parameters=dict(collection_id=collection, venue=venue.name) - ) + + test(collection, venue.name) + # pm.execute_notebook( + # notebook, + # f"{notebook_path}/output/{collection}_{environment}_output_{notebook_name}", + # parameters=dict(collection_id=collection, venue=venue.name) + # ) success.append(collection) except Exception as ex: print(ex) @@ -97,5 +246,6 @@ def run(): with open(fail_outfile, 'w') as the_file: the_file.writelines(x + '\n' for x in fails) + if __name__ == '__main__': run() From da3467de118b680c79b5d36e534af70d8c2f0720 Mon Sep 17 00:00:00 2001 From: jonathansmolenski Date: Tue, 22 Aug 2023 23:29:51 +0000 Subject: [PATCH 03/20] Update uat_associations.txt with new collections --- cmr/uat_associations.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cmr/uat_associations.txt b/cmr/uat_associations.txt index cfde6e34..1d950075 100644 --- a/cmr/uat_associations.txt +++ b/cmr/uat_associations.txt @@ -66,3 +66,4 @@ C1238538240-POCLOUD C1238543220-POCLOUD C1238621111-POCLOUD C1238687282-POCLOUD +C1238658392-POCLOUD From 08e9a27d6dee837ef7c1f1aa8bafbe4840888c3c Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Tue, 22 Aug 2023 16:33:53 -0700 Subject: [PATCH 04/20] changes to drop variables --- notebook_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook_test.py b/notebook_test.py index 3aaa7fb9..773b472f 100644 --- a/notebook_test.py +++ b/notebook_test.py @@ -135,7 +135,7 @@ def test(collection_id, venue): for group in groups: try: - ds = xr.open_dataset(filename, group=group, decode_times=False) + ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) except xr.core.variable.MissingDimensionsError: ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) From 562ede61d305bef17943f91190d5d2d79011dfe7 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 23 Aug 2023 10:02:05 -0700 Subject: [PATCH 05/20] update tests --- notebook_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook_test.py b/notebook_test.py index 773b472f..312bd5c9 100644 --- a/notebook_test.py +++ b/notebook_test.py @@ -3,7 +3,7 @@ import netCDF4 as nc import xarray as xr from harmony import BBox, Client, Collection, Request, Environment -import papermill as pm +#import papermill as pm import argparse from os import path From dfbda3749354e1458b854f2807e51d66d4b5bc35 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 23 Aug 2023 10:19:57 -0700 Subject: [PATCH 06/20] update tests --- notebook_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook_test.py b/notebook_test.py index 312bd5c9..3d9e2b64 100644 --- a/notebook_test.py +++ b/notebook_test.py @@ -106,7 +106,7 @@ def test(collection_id, venue): print('\nWaiting for the job to finish') - harmony_client.wait_for_processing(job1_id, show_progress=True) + #harmony_client.wait_for_processing(job1_id) results = harmony_client.result_json(job1_id) print('\nDownloading results:') From edeb4c022618628faaab969cf60bf1ed748205f0 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 23 Aug 2023 12:08:59 -0700 Subject: [PATCH 07/20] update python libraries --- poetry.lock | 94 +++-------------------------------------------------- 1 file changed, 5 insertions(+), 89 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6c6cf2c5..76c2eb6c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -16,7 +15,6 @@ files = [ name = "ansiwrap" version = "0.8.4" description = "textwrap, but savvy to ANSI colors and styles" -category = "dev" optional = false python-versions = "*" files = [ @@ -31,7 +29,6 @@ textwrap3 = ">=0.9.2" name = "astroid" version = "2.13.2" description = "An abstract syntax tree for Python with inference support." -category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -51,7 +48,6 @@ wrapt = [ name = "attrs" version = "22.2.0" description = "Classes Without Boilerplate" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -70,7 +66,6 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy name = "babel" version = "2.11.0" description = "Internationalization utilities" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -85,7 +80,6 @@ pytz = ">=2015.7" name = "boto3" version = "1.26.51" description = "The AWS SDK for Python" -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -105,7 +99,6 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] name = "botocore" version = "1.29.51" description = "Low-level, data-driven core of boto 3." -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -123,21 +116,19 @@ crt = ["awscrt (==0.15.3)"] [[package]] name = "certifi" -version = "2022.12.7" +version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" files = [ @@ -214,7 +205,6 @@ pycparser = "*" name = "cftime" version = "1.6.2" description = "Time-handling functionality from netcdf4-python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -248,7 +238,6 @@ numpy = ">1.13.3" name = "charset-normalizer" version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = "*" files = [ @@ -346,7 +335,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -361,7 +349,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -373,7 +360,6 @@ files = [ name = "coverage" version = "7.0.5" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -440,7 +426,6 @@ toml = ["tomli"] name = "deprecation" version = "2.1.0" description = "A library to handle automated deprecations" -category = "main" optional = false python-versions = "*" files = [ @@ -455,7 +440,6 @@ packaging = "*" name = "dill" version = "0.3.6" description = "serialize all of python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -470,7 +454,6 @@ graph = ["objgraph (>=1.7.2)"] name = "docutils" version = "0.17.1" description = "Docutils -- Python Documentation Utilities" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -482,7 +465,6 @@ files = [ name = "entrypoints" version = "0.4" description = "Discover and load entry points from installed packages." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -494,7 +476,6 @@ files = [ name = "exceptiongroup" version = "1.1.0" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -509,7 +490,6 @@ test = ["pytest (>=6)"] name = "fastjsonschema" version = "2.16.2" description = "Fastest Python implementation of JSON schema" -category = "dev" optional = false python-versions = "*" files = [ @@ -524,7 +504,6 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc name = "flake8" version = "6.0.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -541,7 +520,6 @@ pyflakes = ">=3.0.0,<3.1.0" name = "harmony-service-lib" version = "1.0.22" description = "A library for Python-based Harmony services to parse incoming messages, fetch data, stage data, and call back to Harmony" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -562,7 +540,6 @@ urllib3 = ">=1.26.9,<1.27.0" name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -574,7 +551,6 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -586,7 +562,6 @@ files = [ name = "importlib-metadata" version = "6.0.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -606,7 +581,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -618,7 +592,6 @@ files = [ name = "isort" version = "5.11.4" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -636,7 +609,6 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -654,7 +626,6 @@ i18n = ["Babel (>=2.7)"] name = "jmespath" version = "1.0.1" description = "JSON Matching Expressions" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -666,7 +637,6 @@ files = [ name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -686,7 +656,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "jupyter-client" version = "7.4.9" description = "Jupyter protocol implementation and client libraries" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -711,7 +680,6 @@ test = ["codecov", "coverage", "ipykernel (>=6.12)", "ipython", "mypy", "pre-com name = "jupyter-core" version = "5.1.3" description = "Jupyter core package. A base package on which Jupyter projects rely." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -732,7 +700,6 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -778,7 +745,6 @@ files = [ name = "markupsafe" version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -838,7 +804,6 @@ files = [ name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -850,7 +815,6 @@ files = [ name = "nbclient" version = "0.7.2" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -860,7 +824,7 @@ files = [ [package.dependencies] jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" nbformat = ">=5.1" traitlets = ">=5.3" @@ -873,7 +837,6 @@ test = ["ipykernel", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>= name = "nbformat" version = "5.7.3" description = "The Jupyter Notebook format" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -895,7 +858,6 @@ test = ["pep440", "pre-commit", "pytest", "testpath"] name = "nest-asyncio" version = "1.5.6" description = "Patch asyncio to allow nested event loops" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -907,7 +869,6 @@ files = [ name = "netcdf4" version = "1.6.2" description = "Provides an object-oriented python interface to the netCDF version 4 library." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -948,7 +909,6 @@ numpy = ">=1.9" name = "numpy" version = "1.24.1" description = "Fundamental package for array computing in Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -986,7 +946,6 @@ files = [ name = "packaging" version = "23.0" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -998,7 +957,6 @@ files = [ name = "papermill" version = "2.4.0" description = "Parametrize and run Jupyter and nteract Notebooks" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1032,7 +990,6 @@ test = ["attrs (>=17.4.0)", "azure-datalake-store (>=0.0.30)", "azure-storage-bl name = "platformdirs" version = "2.6.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1048,7 +1005,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest- name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1064,7 +1020,6 @@ testing = ["pytest", "pytest-benchmark"] name = "pycodestyle" version = "2.10.0" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1076,7 +1031,6 @@ files = [ name = "pycparser" version = "2.21" description = "C parser in Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1088,7 +1042,6 @@ files = [ name = "pyflakes" version = "3.0.1" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1100,7 +1053,6 @@ files = [ name = "pygments" version = "2.14.0" description = "Pygments is a syntax highlighting package written in Python." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1115,7 +1067,6 @@ plugins = ["importlib-metadata"] name = "pylint" version = "2.15.10" description = "python code static checker" -category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -1145,7 +1096,6 @@ testutils = ["gitpython (>3)"] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1172,7 +1122,6 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1209,7 +1158,6 @@ files = [ name = "pystac" version = "0.5.6" description = "Python library for working with Spatiotemporal Asset Catalog (STAC)." -category = "main" optional = false python-versions = "*" files = [ @@ -1227,7 +1175,6 @@ validation = ["jsonschema (==3.2.0)"] name = "pytest" version = "7.2.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1251,7 +1198,6 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2. name = "pytest-cov" version = "4.0.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1270,7 +1216,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1285,7 +1230,6 @@ six = ">=1.5" name = "python-json-logger" version = "2.0.4" description = "A python library adding a json log formatter" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1297,7 +1241,6 @@ files = [ name = "pytz" version = "2022.7.1" description = "World timezone definitions, modern and historical" -category = "dev" optional = false python-versions = "*" files = [ @@ -1309,7 +1252,6 @@ files = [ name = "pywin32" version = "305" description = "Python for Window Extensions" -category = "dev" optional = false python-versions = "*" files = [ @@ -1333,7 +1275,6 @@ files = [ name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1383,7 +1324,6 @@ files = [ name = "pyzmq" version = "25.0.0" description = "Python bindings for 0MQ" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1473,7 +1413,6 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1495,7 +1434,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "s3transfer" version = "0.6.0" description = "An Amazon S3 Transfer Manager" -category = "main" optional = false python-versions = ">= 3.7" files = [ @@ -1513,7 +1451,6 @@ crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"] name = "setuptools" version = "66.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1530,7 +1467,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1542,7 +1478,6 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" optional = false python-versions = "*" files = [ @@ -1554,7 +1489,6 @@ files = [ name = "sphinx" version = "4.5.0" description = "Python documentation generator" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1590,7 +1524,6 @@ test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] name = "sphinx-rtd-theme" version = "1.1.1" description = "Read the Docs theme for Sphinx" -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ @@ -1609,7 +1542,6 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] name = "sphinxcontrib-applehelp" version = "1.0.3" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1625,7 +1557,6 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1641,7 +1572,6 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.0.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1657,7 +1587,6 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1672,7 +1601,6 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1688,7 +1616,6 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1704,7 +1631,6 @@ test = ["pytest"] name = "tenacity" version = "8.1.0" description = "Retry code until it succeeds" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1719,7 +1645,6 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"] name = "textwrap3" version = "0.9.2" description = "textwrap from Python 3.6 backport (plus a few tweaks)" -category = "dev" optional = false python-versions = "*" files = [ @@ -1731,7 +1656,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1743,7 +1667,6 @@ files = [ name = "tomlkit" version = "0.11.6" description = "Style preserving TOML library" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1755,7 +1678,6 @@ files = [ name = "tornado" version = "6.3.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" optional = false python-versions = ">= 3.8" files = [ @@ -1776,7 +1698,6 @@ files = [ name = "tqdm" version = "4.64.1" description = "Fast, Extensible Progress Meter" -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ @@ -1797,7 +1718,6 @@ telegram = ["requests"] name = "traitlets" version = "5.8.1" description = "Traitlets Python configuration system" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1813,7 +1733,6 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] name = "typing-extensions" version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1825,7 +1744,6 @@ files = [ name = "urllib3" version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1842,7 +1760,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "wrapt" version = "1.14.1" description = "Module for decorators, wrappers and monkey patching." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -1916,7 +1833,6 @@ files = [ name = "zipp" version = "3.11.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.7" files = [ From aee35a084378e72a24ddd25866e03324f1e11372 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 23 Aug 2023 12:21:21 -0700 Subject: [PATCH 08/20] update notebook test --- notebook_test.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/notebook_test.py b/notebook_test.py index 3d9e2b64..4a58bfc5 100644 --- a/notebook_test.py +++ b/notebook_test.py @@ -3,7 +3,6 @@ import netCDF4 as nc import xarray as xr from harmony import BBox, Client, Collection, Request, Environment -#import papermill as pm import argparse from os import path @@ -106,11 +105,9 @@ def test(collection_id, venue): print('\nWaiting for the job to finish') - #harmony_client.wait_for_processing(job1_id) results = harmony_client.result_json(job1_id) print('\nDownloading results:') - #filename = harmony_client.download_all(job1_id, overwrite=True)[0].result() futures = harmony_client.download_all(job1_id) file_names = [f.result() for f in futures] @@ -134,10 +131,8 @@ def test(collection_id, venue): groups = [None] for group in groups: - try: - ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) - except xr.core.variable.MissingDimensionsError: - ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) + + ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) assert len(ds.coords['subset_index']) == max_results variables = list(ds.variables) @@ -204,9 +199,6 @@ def run(): inputFile = _args.input_file output_location = _args.output_path if _args.output_path else '.' - #notebook_path = path.realpath(path.dirname(notebook)) - #notebook_name = path.basename(notebook) - success = [] fails = [] @@ -221,13 +213,7 @@ def run(): try: print(collection) - test(collection, venue.name) - # pm.execute_notebook( - # notebook, - # f"{notebook_path}/output/{collection}_{environment}_output_{notebook_name}", - # parameters=dict(collection_id=collection, venue=venue.name) - # ) success.append(collection) except Exception as ex: print(ex) From d5fc3e11f58ce85883c6a1250f1a45e2325e698e Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 23 Aug 2023 12:55:42 -0700 Subject: [PATCH 09/20] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d078b13..8efd2f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Issue-68 - Updated jupyter notebook + - Update notebook test to use python code directly instead of using jupyter notebook + - Updated python libraries ### Deprecated ### Removed ### Fixed From 39a61f0e26f5c097aefaab0f5269f7d52b99c137 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Wed, 23 Aug 2023 13:31:35 -0700 Subject: [PATCH 10/20] update tests names --- .github/workflows/jupyter_test.yml | 18 ++++++++---------- notebook_test.py => add_collection_test.py | 0 2 files changed, 8 insertions(+), 10 deletions(-) rename notebook_test.py => add_collection_test.py (100%) diff --git a/.github/workflows/jupyter_test.yml b/.github/workflows/jupyter_test.yml index 1866a4db..74a998c8 100644 --- a/.github/workflows/jupyter_test.yml +++ b/.github/workflows/jupyter_test.yml @@ -1,5 +1,5 @@ # This is a test pipeline, that verifies and updates the associations in cmr -name: Jupyter Test +name: Add Collection Test # Controls when the workflow will run on: # Allows you to run this workflow manually from the Actions tab @@ -40,9 +40,7 @@ jobs: - name: Install dependencies run: | pip3 install --upgrade pip - pip3 install papermill pip3 install xarray - pip3 install jupyter pip3 install black pip3 install matplotlib pip3 install netCDF4 @@ -54,10 +52,10 @@ jobs: run: | cmr_association_diff -e uat -t service -a "cmr/uat_associations.txt" -p POCLOUD -n 'PODAAC Concise' -o ${{ env.UAT_OUTPUT_FILE }} --token ${{ secrets.LAUNCHPAD_TOKEN_UAT }} cmr_association_diff -e ops -t service -a "cmr/ops_associations.txt" -p POCLOUD -n 'PODAAC Concise' -o ${{ env.OPS_OUTPUT_FILE }} --token ${{ secrets.LAUNCHPAD_TOKEN_OPS }} - - name: Run Jupyter notebooks + - name: Run Add Collection Test run: | - python3 "notebook_test.py" -n "jupyter_notebooks/harmony_concise_api_test.ipynb" -e uat -i ${{ env.UAT_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} - python3 "notebook_test.py" -n "jupyter_notebooks/harmony_concise_api_test.ipynb" -e ops -i ${{ env.OPS_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} + python3 "add_collection_test.py" -e uat -i ${{ env.UAT_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} + python3 "add_collection_test.py" -e ops -i ${{ env.OPS_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} - name: Check UAT files id: check_UAT_output_files run: | @@ -90,22 +88,22 @@ jobs: if: | steps.check_UAT_output_files.outputs.SUCCESS_EXISTS == 'true' run: | - echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\nJupyter-test UAT result: SUCCESS\nSuccessfully ran Concise Notebook on the following collections:\n${{ steps.check_UAT_output_files.outputs.SUCCESS_CONTENT }}\n" >> $GITHUB_ENV + echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\ncollection-test UAT result: SUCCESS\nSuccessfully ran Concise Notebook on the following collections:\n${{ steps.check_UAT_output_files.outputs.SUCCESS_CONTENT }}\n" >> $GITHUB_ENV - name: Slack Report update - Add info for UAT check - FAILURE if: | steps.check_UAT_output_files.outputs.FAIL_EXISTS == 'true' run: | - echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\nJupyter-test UAT result: FAILURE\nFailed to run Concise Notebook on the following collections:\n${{ steps.check_UAT_output_files.outputs.FAIL_CONTENT }}\n" >> $GITHUB_ENV + echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\ncollection-test UAT result: FAILURE\nFailed to run Concise Notebook on the following collections:\n${{ steps.check_UAT_output_files.outputs.FAIL_CONTENT }}\n" >> $GITHUB_ENV - name: Slack Report update - Add info for OPS check - SUCCESS if: | steps.check_OPS_output_files.outputs.SUCCESS_EXISTS == 'true' run: | - echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\nJupyter-test OPS result: SUCCESS\nSuccessfully ran Concise Notebook on the following collections:\n${{ steps.check_OPS_output_files.outputs.SUCCESS_CONTENT }}\n" >> $GITHUB_ENV + echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\ncollection-test OPS result: SUCCESS\nSuccessfully ran Concise Notebook on the following collections:\n${{ steps.check_OPS_output_files.outputs.SUCCESS_CONTENT }}\n" >> $GITHUB_ENV - name: Slack Report update - Add info for OPS check - FAILURE if: | steps.check_OPS_output_files.outputs.FAIL_EXISTS == 'true' run: | - echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\nJupyter-test OPS result: FAILURE\nFailed to run Concise Notebook on the following collections:\n${{ steps.check_OPS_output_files.outputs.FAIL_CONTENT }}\n" >> $GITHUB_ENV + echo "SLACK_REPORT_CONTENT=${{ env.SLACK_REPORT_CONTENT }}\ncollection-test OPS result: FAILURE\nFailed to run Concise Notebook on the following collections:\n${{ steps.check_OPS_output_files.outputs.FAIL_CONTENT }}\n" >> $GITHUB_ENV - name: Slack Report update - Add action link on Failure if: | steps.check_UAT_output_files.outputs.FAIL_EXISTS == 'true' || diff --git a/notebook_test.py b/add_collection_test.py similarity index 100% rename from notebook_test.py rename to add_collection_test.py From 9de25e370156c82949b4f537258d5535135c0aff Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 10:03:28 -0700 Subject: [PATCH 11/20] fix checking venue to lower case --- add_collection_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/add_collection_test.py b/add_collection_test.py index 4a58bfc5..8fe16a4d 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -51,9 +51,9 @@ def parse_args(): def get_username_and_password(venue): - if venue == "UAT": + if venue.lower() == "uat": return os.environ.get("UAT_USERNAME"), os.environ.get("UAT_PASSWORD") - elif venue == "OPS": + elif venue.lower() == "ops": return os.environ.get('OPS_USERNAME'), os.environ.get('OPS_PASSWORD') else: raise ValueError("Invalid venue") From 832f8098d2c2f4f4ebdde5b851a1b0081387f264 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 10:16:32 -0700 Subject: [PATCH 12/20] debugging tests --- .github/workflows/jupyter_test.yml | 4 ++-- add_collection_test.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jupyter_test.yml b/.github/workflows/jupyter_test.yml index 74a998c8..75e0de6b 100644 --- a/.github/workflows/jupyter_test.yml +++ b/.github/workflows/jupyter_test.yml @@ -54,8 +54,8 @@ jobs: cmr_association_diff -e ops -t service -a "cmr/ops_associations.txt" -p POCLOUD -n 'PODAAC Concise' -o ${{ env.OPS_OUTPUT_FILE }} --token ${{ secrets.LAUNCHPAD_TOKEN_OPS }} - name: Run Add Collection Test run: | - python3 "add_collection_test.py" -e uat -i ${{ env.UAT_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} - python3 "add_collection_test.py" -e ops -i ${{ env.OPS_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} + python3 add_collection_test.py -e uat -i ${{ env.UAT_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} + python3 add_collection_test.py -e ops -i ${{ env.OPS_OUTPUT_FILE }} -o ${{ env.OUTPUT_DIR }} - name: Check UAT files id: check_UAT_output_files run: | diff --git a/add_collection_test.py b/add_collection_test.py index 8fe16a4d..bf6bf944 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -234,4 +234,5 @@ def run(): if __name__ == '__main__': + print("Start running test .......") run() From ce09159f6c8d23a6530401f729e4e6b3fe0ce417 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 10:33:49 -0700 Subject: [PATCH 13/20] testing larger runners --- .github/workflows/jupyter_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jupyter_test.yml b/.github/workflows/jupyter_test.yml index 75e0de6b..f5117e44 100644 --- a/.github/workflows/jupyter_test.yml +++ b/.github/workflows/jupyter_test.yml @@ -10,7 +10,7 @@ jobs: build: name: Test Execution # The type of runner that the job will run on - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 2xlarge steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 From 5f0b742e2b5185e5689020d29a5fc1e6af366fb7 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 10:42:01 -0700 Subject: [PATCH 14/20] debug tests --- .github/workflows/jupyter_test.yml | 2 +- add_collection_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jupyter_test.yml b/.github/workflows/jupyter_test.yml index f5117e44..75e0de6b 100644 --- a/.github/workflows/jupyter_test.yml +++ b/.github/workflows/jupyter_test.yml @@ -10,7 +10,7 @@ jobs: build: name: Test Execution # The type of runner that the job will run on - runs-on: ubuntu-20.04 2xlarge + runs-on: ubuntu-latest steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 diff --git a/add_collection_test.py b/add_collection_test.py index bf6bf944..fc83ffcd 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -235,4 +235,4 @@ def run(): if __name__ == '__main__': print("Start running test .......") - run() + #run() From 863390e771f6916c0618b50bae88d5fbfb5e12b7 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 12:04:04 -0700 Subject: [PATCH 15/20] debug test --- add_collection_test.py | 82 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/add_collection_test.py b/add_collection_test.py index fc83ffcd..7f32ac08 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -151,38 +151,38 @@ def test(collection_id, venue): if x_var is None or y_var is None: raise Exception("Lon and Lat variables are not found") - for index in range(0, max_results): - ax = ds.isel(subset_index=index).plot.scatter( - y=y_var, - x=x_var, - hue=variable, - s=1, - levels=9, - cmap="jet", - aspect=2.5, - size=9 - ) - plt.xlim(0., 360.) - plt.ylim(-90., 90.) - plt.show(block=False) - plt.close(ax.figure) - - ax = ds.plot.scatter( - y=y_var, - x=x_var, - hue=variable, - s=1, - levels=9, - cmap="jet", - aspect=2.5, - size=9 - ) - plt.xlim(0., 360.) - plt.ylim(-90., 90.) - plt.show(block=False) - plt.close(ax.figure) - - ds.close() +# for index in range(0, max_results): +# ax = ds.isel(subset_index=index).plot.scatter( +# y=y_var, +# x=x_var, +# hue=variable, +# s=1, +# levels=9, +# cmap="jet", +# aspect=2.5, +# size=9 +# ) +# plt.xlim(0., 360.) +# plt.ylim(-90., 90.) +# plt.show(block=False) +# plt.close(ax.figure) +# +# ax = ds.plot.scatter( +# y=y_var, +# x=x_var, +# hue=variable, +# s=1, +# levels=9, +# cmap="jet", +# aspect=2.5, +# size=9 +# ) +# plt.xlim(0., 360.) +# plt.ylim(-90., 90.) +# plt.show(block=False) +# plt.close(ax.figure) +# +# ds.close() def run(): @@ -220,17 +220,17 @@ def run(): fails.append(collection) # Create output files - if output_location: - success_outfile = path.realpath(f'{output_location}/{_args.env}_success.txt') - fail_outfile = path.realpath(f'{output_location}/{_args.env}_fail.txt') + #if output_location: + # success_outfile = path.realpath(f'{output_location}/{_args.env}_success.txt') + # fail_outfile = path.realpath(f'{output_location}/{_args.env}_fail.txt') - if success: - with open(success_outfile, 'w') as the_file: - the_file.writelines(x + '\n' for x in success) + # if success: + # with open(success_outfile, 'w') as the_file: + # the_file.writelines(x + '\n' for x in success) - if fails: - with open(fail_outfile, 'w') as the_file: - the_file.writelines(x + '\n' for x in fails) + # if fails: + # with open(fail_outfile, 'w') as the_file: + # the_file.writelines(x + '\n' for x in fails) if __name__ == '__main__': From 01e37ec5da6f6ad3d9c7558e7484e333e270e048 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 12:41:11 -0700 Subject: [PATCH 16/20] debug test --- add_collection_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add_collection_test.py b/add_collection_test.py index 7f32ac08..9b199a57 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -235,4 +235,4 @@ def run(): if __name__ == '__main__': print("Start running test .......") - #run() + run() From 5ab3d0ca72289852b471714cabec6ed61dcd0ff3 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 12:59:11 -0700 Subject: [PATCH 17/20] debug test --- add_collection_test.py | 49 ++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/add_collection_test.py b/add_collection_test.py index 9b199a57..eb5d308a 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -151,39 +151,22 @@ def test(collection_id, venue): if x_var is None or y_var is None: raise Exception("Lon and Lat variables are not found") -# for index in range(0, max_results): -# ax = ds.isel(subset_index=index).plot.scatter( -# y=y_var, -# x=x_var, -# hue=variable, -# s=1, -# levels=9, -# cmap="jet", -# aspect=2.5, -# size=9 -# ) -# plt.xlim(0., 360.) -# plt.ylim(-90., 90.) -# plt.show(block=False) -# plt.close(ax.figure) -# -# ax = ds.plot.scatter( -# y=y_var, -# x=x_var, -# hue=variable, -# s=1, -# levels=9, -# cmap="jet", -# aspect=2.5, -# size=9 -# ) -# plt.xlim(0., 360.) -# plt.ylim(-90., 90.) -# plt.show(block=False) -# plt.close(ax.figure) -# -# ds.close() - + for index in range(0, max_results): + ax = ds.isel(subset_index=index).plot.scatter( + y=y_var, + x=x_var, + hue=variable, + s=1, + levels=9, + cmap="jet", + aspect=2.5, + size=9 + ) + plt.xlim(0., 360.) + plt.ylim(-90., 90.) + plt.show(block=False) + plt.clf() + plt.close(ax.figure) def run(): """ From 47c483760393f3f32bb54876c39651b21976e412 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Thu, 24 Aug 2023 13:22:25 -0700 Subject: [PATCH 18/20] debug test --- add_collection_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add_collection_test.py b/add_collection_test.py index eb5d308a..370d42dc 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -164,7 +164,7 @@ def test(collection_id, venue): ) plt.xlim(0., 360.) plt.ylim(-90., 90.) - plt.show(block=False) + #plt.show(block=False) plt.clf() plt.close(ax.figure) From 5a77f3b0ef7ed13484d4a7091992049f0649b054 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Mon, 28 Aug 2023 12:48:01 -0700 Subject: [PATCH 19/20] updated concise tests --- .github/workflows/build-pipeline.yml | 6 +- .github/workflows/jupyter_test.yml | 2 - add_collection_test.py | 214 +++++++++++++++++++-------- 3 files changed, 156 insertions(+), 66 deletions(-) diff --git a/.github/workflows/build-pipeline.yml b/.github/workflows/build-pipeline.yml index 9abd7805..f6c306a4 100644 --- a/.github/workflows/build-pipeline.yml +++ b/.github/workflows/build-pipeline.yml @@ -141,17 +141,19 @@ jobs: git tag -a "${{ env.software_version }}" -m "Version ${{ env.software_version }}" git push origin "${{ env.software_version }}" - name: Publish UMM-S with new version - uses: podaac/cmr-umm-updater@0.2.3 + uses: podaac/cmr-umm-updater@0.5.0 if: | github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release') with: - umm-s-json: 'cmr/concise_cmr_umm_s.json' + umm-json: 'cmr/concise_cmr_umm_s.json' provider: 'POCLOUD' env: ${{ env.venue }} version: ${{ env.software_version }} timeout: 60 disable_removal: 'true' + umm_type: 'umm-s' + use_associations: 'false' env: cmr_user: ${{secrets.CMR_USER}} cmr_pass: ${{secrets.CMR_PASS}} diff --git a/.github/workflows/jupyter_test.yml b/.github/workflows/jupyter_test.yml index 75e0de6b..7eefcace 100644 --- a/.github/workflows/jupyter_test.yml +++ b/.github/workflows/jupyter_test.yml @@ -40,9 +40,7 @@ jobs: - name: Install dependencies run: | pip3 install --upgrade pip - pip3 install xarray pip3 install black - pip3 install matplotlib pip3 install netCDF4 pip3 install git+https://github.com/nasa/harmony-py.git pip3 install git+https://github.com/podaac/cmr-umm-updater.git diff --git a/add_collection_test.py b/add_collection_test.py index 370d42dc..bcd210a4 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -1,15 +1,15 @@ import os -import matplotlib.pyplot as plt +from os import path +from urllib.parse import urlparse +import itertools +import unittest +import numpy as np import netCDF4 as nc -import xarray as xr +import requests from harmony import BBox, Client, Collection, Request, Environment import argparse - -from os import path - from utils import FileHandler from utils.enums import Venue -import itertools def parse_args(): @@ -75,12 +75,115 @@ def get_x_y_variables(variables): return x_var, y_var +def verify_dims(merged_group, origin_group, both_merged): + for dim in origin_group.dimensions: + if both_merged: + unittest.TestCase().assertEqual(merged_group.dimensions[dim].size, origin_group.dimensions[dim].size) + else: + unittest.TestCase().assertGreaterEqual(merged_group.dimensions[dim].size, origin_group.dimensions[dim].size) + + +def verify_attrs(merged_obj, origin_obj, both_merged): + ignore_attributes = [ + 'request-bounding-box', 'request-bounding-box-description', 'PODAAC-dataset-shortname', + 'PODAAC-persistent-ID', 'time_coverage_end', 'time_coverage_start' + ] + + merged_attrs = merged_obj.ncattrs() + origin_attrs = origin_obj.ncattrs() + + for attr in origin_attrs: + if attr in ignore_attributes: + # Skip attributes which are present in the Java implementation, + # but not (currently) present in the Python implementation + continue + + if not both_merged and attr not in merged_attrs: + # Skip attributes which are not present in both merged and origin. + # This is normal operation as some attributes may be omited b/c + # they're inconsistent between granules + continue + + merged_attr = merged_obj.getncattr(attr) + if both_merged and isinstance(merged_attr, int): + # Skip integer values - the Java implementation seems to omit + # these values due to its internal handling of all values as + # Strings + continue + + origin_attr = origin_obj.getncattr(attr) + if isinstance(origin_attr, np.ndarray): + unittest.TestCase().assertTrue(np.array_equal(merged_attr, origin_attr)) + else: + if attr != "history_json": + unittest.TestCase().assertEqual(merged_attr, origin_attr) + + +def verify_variables(merged_group, origin_group, subset_index, both_merged): + for var in origin_group.variables: + merged_var = merged_group.variables[var] + origin_var = origin_group.variables[var] + + verify_attrs(merged_var, origin_var, both_merged) + + if both_merged: + # both groups require subset indexes + merged_data = merged_var[subset_index[0]] + origin_data = origin_var[subset_index[1]] + else: + # merged group requires a subset index + merged_data = np.resize(merged_var[subset_index], origin_var.shape) + origin_data = origin_var + + # verify variable data + if isinstance(origin_data, str): + unittest.TestCase().assertEqual(merged_data, origin_data) + else: + unittest.TestCase().assertTrue(np.array_equal(merged_data, origin_data, equal_nan=True)) + + +def verify_groups(merged_group, origin_group, subset_index, both_merged=False): + verify_dims(merged_group, origin_group, both_merged) + verify_attrs(merged_group, origin_group, both_merged) + verify_variables(merged_group, origin_group, subset_index, both_merged) + + for child_group in origin_group.groups: + merged_subgroup = merged_group[child_group] + origin_subgroup = origin_group[child_group] + verify_groups(merged_subgroup, origin_subgroup, subset_index, both_merged) + + +# GET TOKEN FROM CMR +def get_token(cmr_root, username, password): + token_api = "https://{}/api/users/tokens".format(cmr_root) + response = requests.get(token_api, auth=(username, password)) + content = response.json() + if len(content) > 0: + return content[0].get('access_token') + else: + create_token_api = "https://{}/api/users/token".format(cmr_root) + response = requests.post(create_token_api, auth=(username, password)) + content = response.json() + return content.get('access_token') + + +def download_file(url, local_path, headers): + response = requests.get(url, stream=True, headers=headers) + if response.status_code == 200: + with open(local_path, 'wb') as file: + for chunk in response.iter_content(chunk_size=8192): + file.write(chunk) + print("Original File downloaded successfully.") + else: + print(f"Failed to download the file. Status code: {response.status_code}") + + def test(collection_id, venue): max_results = 2 username, password = get_username_and_password(venue) - environment = Environment.UAT if venue == "UAT" else Environment.PROD + environment = Environment.UAT if venue.lower() == "uat" else Environment.PROD harmony_client = Client(auth=(username, password), env=environment) collection = Collection(id=collection_id) @@ -115,58 +218,45 @@ def test(collection_id, venue): filename = file_names[0] # Handle time dimension and variables dropping - dt = nc.Dataset(filename, 'r') - groups = list(dt.groups) - dt.close() - - drop_variables = [ - 'time', - 'sample', - 'meas_ind', - 'wvf_ind', - 'ddm', - 'averaged_l1' - ] - if not groups: - groups = [None] + merge_dataset = nc.Dataset(filename, 'r') - for group in groups: + cmr_base_url = "https://cmr.earthdata.nasa.gov/search/granules.umm_json?readable_granule_name=" + edl_root = 'urs.earthdata.nasa.gov' - ds = xr.open_dataset(filename, group=group, decode_times=False, drop_variables=drop_variables) + if venue.lower() == 'uat': + cmr_base_url = "https://cmr.uat.earthdata.nasa.gov/search/granules.umm_json?readable_granule_name=" + edl_root = 'uat.urs.earthdata.nasa.gov' + + token = get_token(edl_root, username, password) + headers = { + "Authorization": f"Bearer {token}" + } - assert len(ds.coords['subset_index']) == max_results - variables = list(ds.variables) - x_var, y_var = get_x_y_variables(variables) + original_files = merge_dataset.variables['subset_files'] + assert len(original_files) == max_results - for v in variables: - if v not in ['subset_files', 'lat', 'lon', 'latitude', 'longitude', 'beam_clat', 'beam_clon']: - variable = v - break + for file in original_files: - if x_var is not None and y_var is not None: - break + file_name = file.rsplit(".", 1)[0] + print(file_name) + cmr_query = f"{cmr_base_url}{file_name}&collection_concept_id={collection_id}" + print(cmr_query) + + response = requests.get(cmr_query, headers=headers) + + result = response.json() + links = result.get('items')[0].get('umm').get('RelatedUrls') + for link in links: + if link.get('Type') == 'GET DATA': + data_url = link.get('URL') + parsed_url = urlparse(data_url) + local_file_name = os.path.basename(parsed_url.path) + download_file(data_url, local_file_name, headers) + + for i, file in enumerate(original_files): + origin_dataset = nc.Dataset(file) + verify_groups(merge_dataset, origin_dataset, i) - ds.close() - - if x_var is None or y_var is None: - raise Exception("Lon and Lat variables are not found") - - for index in range(0, max_results): - ax = ds.isel(subset_index=index).plot.scatter( - y=y_var, - x=x_var, - hue=variable, - s=1, - levels=9, - cmap="jet", - aspect=2.5, - size=9 - ) - plt.xlim(0., 360.) - plt.ylim(-90., 90.) - #plt.show(block=False) - plt.clf() - plt.close(ax.figure) def run(): """ @@ -203,17 +293,17 @@ def run(): fails.append(collection) # Create output files - #if output_location: - # success_outfile = path.realpath(f'{output_location}/{_args.env}_success.txt') - # fail_outfile = path.realpath(f'{output_location}/{_args.env}_fail.txt') + if output_location: + success_outfile = path.realpath(f'{output_location}/{_args.env}_success.txt') + fail_outfile = path.realpath(f'{output_location}/{_args.env}_fail.txt') - # if success: - # with open(success_outfile, 'w') as the_file: - # the_file.writelines(x + '\n' for x in success) + if success: + with open(success_outfile, 'w') as the_file: + the_file.writelines(x + '\n' for x in success) - # if fails: - # with open(fail_outfile, 'w') as the_file: - # the_file.writelines(x + '\n' for x in fails) + if fails: + with open(fail_outfile, 'w') as the_file: + the_file.writelines(x + '\n' for x in fails) if __name__ == '__main__': From b4bc67e87831010df807e6e0bbefe13ef22bd1b6 Mon Sep 17 00:00:00 2001 From: Simon Liu Date: Mon, 28 Aug 2023 13:03:20 -0700 Subject: [PATCH 20/20] fix indentation --- add_collection_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/add_collection_test.py b/add_collection_test.py index bcd210a4..dc80d0d1 100644 --- a/add_collection_test.py +++ b/add_collection_test.py @@ -226,7 +226,7 @@ def test(collection_id, venue): if venue.lower() == 'uat': cmr_base_url = "https://cmr.uat.earthdata.nasa.gov/search/granules.umm_json?readable_granule_name=" edl_root = 'uat.urs.earthdata.nasa.gov' - + token = get_token(edl_root, username, password) headers = { "Authorization": f"Bearer {token}" @@ -293,7 +293,7 @@ def run(): fails.append(collection) # Create output files - if output_location: + if output_location: success_outfile = path.realpath(f'{output_location}/{_args.env}_success.txt') fail_outfile = path.realpath(f'{output_location}/{_args.env}_fail.txt')