cloudcatalog 0.4__tar.gz → 0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. cloudcatalog-0.5/.github/bug_report.md +38 -0
  2. cloudcatalog-0.5/.github/feature_request.md +20 -0
  3. {cloudcatalog-0.4/src/cloudcatalog.egg-info → cloudcatalog-0.5}/PKG-INFO +7 -8
  4. {cloudcatalog-0.4 → cloudcatalog-0.5}/README.md +3 -3
  5. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/Notes.md +1 -1
  6. cloudcatalog-0.5/docs/cloudcatalog-spec-05.md +398 -0
  7. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/cloudcatalog_demo.py +0 -2
  8. {cloudcatalog-0.4 → cloudcatalog-0.5}/pyproject.toml +3 -3
  9. {cloudcatalog-0.4 → cloudcatalog-0.5/src/cloudcatalog.egg-info}/PKG-INFO +7 -8
  10. {cloudcatalog-0.4 → cloudcatalog-0.5}/src/cloudcatalog.egg-info/SOURCES.txt +6 -3
  11. cloudcatalog-0.5/src/cloudcatalog.egg-info/top_level.txt +5 -0
  12. {cloudcatalog-0.4 → cloudcatalog-0.5}/src/cloudcatalog.py +150 -17
  13. cloudcatalog-0.5/src/quickfail.py +9 -0
  14. cloudcatalog-0.4/LICENSE +0 -21
  15. cloudcatalog-0.4/src/cloudcatalog.egg-info/top_level.txt +0 -4
  16. {cloudcatalog-0.4 → cloudcatalog-0.5}/.gitignore +0 -0
  17. /cloudcatalog-0.4/LICENSE.txt → /cloudcatalog-0.5/LICENSE.MD +0 -0
  18. {cloudcatalog-0.4 → cloudcatalog-0.5}/TODO.md +0 -0
  19. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/Makefile +0 -0
  20. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/conf.py +0 -0
  21. {cloudcatalog-0.4/docs → cloudcatalog-0.5/docs/earlier}/cloudcatalog-spec-04.md +0 -0
  22. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/index.rst +0 -0
  23. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/make.bat +0 -0
  24. {cloudcatalog-0.4 → cloudcatalog-0.5}/docs/scr_logo.png +0 -0
  25. {cloudcatalog-0.4 → cloudcatalog-0.5}/setup.cfg +0 -0
  26. {cloudcatalog-0.4 → cloudcatalog-0.5}/src/__init__.py +0 -0
  27. {cloudcatalog-0.4 → cloudcatalog-0.5}/src/cloudcatalog.egg-info/dependency_links.txt +0 -0
  28. {cloudcatalog-0.4 → cloudcatalog-0.5}/src/cloudcatalog.egg-info/requires.txt +0 -0
  29. {cloudcatalog-0.4 → cloudcatalog-0.5}/src/data/README.rst +0 -0
  30. {cloudcatalog-0.4 → cloudcatalog-0.5}/tests/__init__.py +0 -0
  31. {cloudcatalog-0.4 → cloudcatalog-0.5}/tests/test_catalog_registry.py +0 -0
  32. {cloudcatalog-0.4 → cloudcatalog-0.5}/tests/test_entire_catalog_search.py +0 -0
  33. {cloudcatalog-0.4 → cloudcatalog-0.5}/tests/test_file_registry.py +0 -0
  34. {cloudcatalog-0.4 → cloudcatalog-0.5}/tests/validation.py +0 -0
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Screenshots**
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ **Error Log**
27
+ If applicable, add the log dump produced by the bug.
28
+
29
+ **Desktop (please complete the following information):**
30
+ - OS: [e.g. iOS]
31
+ - Browser [e.g. chrome, safari] (if applicable)
32
+ - Platform version [e.g. 1.0.0]
33
+ - Python version
34
+ - AWS CLI version
35
+
36
+
37
+ **Additional context**
38
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -1,20 +1,19 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudcatalog
3
- Version: 0.4
3
+ Version: 0.5
4
4
  Summary: API for accessing the generalized Cloud Catalog (cloudcatalog) specification for sharing data in and across clouds
5
5
  Author-email: Johns Hopkins University Applied Physics Laboratory LLC <sandy.antunes@jhuapl.edu>
6
6
  License: MIT License
7
7
  Project-URL: Homepage, https://heliocloud.org
8
- Project-URL: Documentation, https://heliocloud.org
9
- Project-URL: Repository, https://gitlab.smce.nasa.gov
8
+ Project-URL: Documentation, https://github.com/heliocloud-data/cloudcatalog
9
+ Project-URL: Repository, https://github.com/heliocloud-data/cloudcatalog
10
10
  Keywords: cloud,index,catalog,AWS
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Topic :: Utilities
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
- License-File: LICENSE
17
- License-File: LICENSE.txt
16
+ License-File: LICENSE.MD
18
17
  Requires-Dist: boto3
19
18
  Requires-Dist: pandas
20
19
 
@@ -66,17 +65,17 @@ print(fr.get_entries())
66
65
  # also save the downloaded file index
67
66
  fr_id = 'a_dataset_id_from_the_catalog'
68
67
  start_date = '2007-02-01T00:00:00Z' # A ISO 8601 standard time and a valid time witin the mission/file-index
69
- end_date = None # A ISO 8601 standard time or None if want all the file indices after start_date
68
+ stop_date = None # A ISO 8601 standard time or None if want all the file indices after start_date
70
69
  myfiles = fr.request_cloud_catalog(fr_id, start_date=start_date, end_date=end_date, overwrite=False)
71
70
  ```
72
71
 
73
72
  ### Streaming Data from the File Catalog
74
- You now have a pandas DataFrame with startdate, key, and filesize for all the files of the mission within your specified start and end dates. From here, you can use the key to stream some of the data through EC2, a Lambda, or other processing methods.
73
+ You now have a pandas DataFrame with startdate, stopdate, key, and filesize for all the files of the mission within your specified start and end dates. From here, you can use the key to stream some of the data through EC2, a Lambda, or other processing methods.
75
74
 
76
75
  This tool also offers a simple function for streaming the data once the file catalog is obtained:
77
76
 
78
77
  ```python
79
- cloudcatalog.CloudCatalog.stream(cloud_catalog, lambda bfile, startdate, filesize: print(len(bo.read()), filesize))
78
+ cloudcatalog.CloudCatalog.stream(cloud_catalog, lambda bfile, startdate, stopdate, filesize: print(len(bo.read()), filesize))
80
79
  ```
81
80
 
82
81
  ### Searching the Entire Catalog
@@ -46,17 +46,17 @@ print(fr.get_entries())
46
46
  # also save the downloaded file index
47
47
  fr_id = 'a_dataset_id_from_the_catalog'
48
48
  start_date = '2007-02-01T00:00:00Z' # A ISO 8601 standard time and a valid time witin the mission/file-index
49
- end_date = None # A ISO 8601 standard time or None if want all the file indices after start_date
49
+ stop_date = None # A ISO 8601 standard time or None if want all the file indices after start_date
50
50
  myfiles = fr.request_cloud_catalog(fr_id, start_date=start_date, end_date=end_date, overwrite=False)
51
51
  ```
52
52
 
53
53
  ### Streaming Data from the File Catalog
54
- You now have a pandas DataFrame with startdate, key, and filesize for all the files of the mission within your specified start and end dates. From here, you can use the key to stream some of the data through EC2, a Lambda, or other processing methods.
54
+ You now have a pandas DataFrame with startdate, stopdate, key, and filesize for all the files of the mission within your specified start and end dates. From here, you can use the key to stream some of the data through EC2, a Lambda, or other processing methods.
55
55
 
56
56
  This tool also offers a simple function for streaming the data once the file catalog is obtained:
57
57
 
58
58
  ```python
59
- cloudcatalog.CloudCatalog.stream(cloud_catalog, lambda bfile, startdate, filesize: print(len(bo.read()), filesize))
59
+ cloudcatalog.CloudCatalog.stream(cloud_catalog, lambda bfile, startdate, stopdate, filesize: print(len(bo.read()), filesize))
60
60
  ```
61
61
 
62
62
  ### Searching the Entire Catalog
@@ -9,7 +9,7 @@ Test the wheel
9
9
 
10
10
  Do a test package upload then install with:
11
11
 
12
- * twine upload -repository testpypi dist/*
12
+ * twine upload -r testpypi dist/*
13
13
  * pip install -i https://test.pypi.org/simple/cloudcatalog==0.4
14
14
 
15
15
  Commit with
@@ -0,0 +1,398 @@
1
+ <!-- TOC -->
2
+ [1 Introduction](#1-intro)<br/>
3
+ [2 Global data registry](#2-dataregistry)<br/>
4
+ [3 Catalog](#3-catalog)<br/>
5
+ [4 File Indices](#4-fileindex)<br/>
6
+ [5 Info Metadata](#5-info)<br/>
7
+ <!-- \TOC -->
8
+
9
+ Version 0.5.0 \| HelioCloud \|
10
+
11
+ # 1 The generalized Cloud Catalog specification for HelioCloud
12
+
13
+ The shared Cloud Catalog specification can be used for sharing datasets across cloud frameworks.
14
+
15
+ For HelioCloud, this specification creates a global data registry ('HelioDataRegistry'), maintained at the HDRL GitLab repository, then defines the file catalogs ('cloudCatalog') for each dataset, that resides in the S3 (or equivalent) bucket alongside the dataset.
16
+
17
+ That 'HelioDataRegistry.json' is a JSON file consisting of **name** and **endpoint** and lists buckets as endpoints, not datasets. Tools can visit each **endpoint** to get the **catalog.json** listing datasets available in that bucket. The 'cloudCatalog' consists of the required index to the actual files, and an optional dataset summary file. The cloudCatalog itself is a set of ***<id>_YYYY.csv*** (or csp-zip or parquet) index files, one per year. The optional summary file is named **<id>.json** file and provides additional potentially searchable metadata.
18
+
19
+ ## 1.1 Flow
20
+
21
+ Findability is through the 'HelioDataRegistry' JSON index of S3 buckets, which leads to the **catalog.json** for each S3 bucket that indicates available datasets, which points to the individual dataset file catalog index files **<id>_YYYY.csv** and its optional associated **<id>.info** metadata auxillary file.
22
+
23
+ (Diagram here) github.com/whatever/HelioDataRegistry.json -> s3://aplcloud.com/mybucket/catalog.json -> individual dataset file catalogs (.csv)
24
+
25
+ ![Data Registry Schema](dataRegistry_diagram.png)
26
+
27
+ Datasets are findable by going to 'HelioDataRegistry.json' to get endpoints, then visiting each S3 endpoint to get the catalog listing of datasets available.
28
+
29
+ Accessing the actual data involves accessing each dataset's catalog index files. The cloudCatalog consists of CSV files, one per year. Therefore, a user has the choice to:
30
+ * (a) download the CSV file
31
+ * (b) use our provided Python API to fetch a subset of the CSV file, by date range
32
+ * (c) use AWS Athena to run queries on the CSV file
33
+
34
+ ## 1.2 Uploading Data
35
+
36
+ Data providers must provide both the Data. the Dataset Description (in .json as per the catalog.json spec) and a Manifest (in the .csv format we specify).
37
+
38
+ We will generate an S3 Inventory of the contents and check against their Manifest, and send them the 'diff' file. It is their responsibility to provide us with a corrected Manifest. This iterates until their Manifest matches the actual Data provided.
39
+
40
+ ## 1.3 Refreshing Data
41
+
42
+ Periodic updates to datasets go into an upload bucket. The process is generally the same as for Uploading Data: provide Data and Manifest. Once the Manifest matches the Data, we move the data from the upload bucket to the proper location and append the new Manifest to the existing Manifests.
43
+
44
+ ## 1.4 Scope
45
+
46
+ We will produce Python tools that fit this API; a web form or similar tool for users to register datasets; automation to move datasets from an upload bucket to the data buckets; scripts to generate the AWS Inventory and compare against a user-provided Manifest; a sample use of Athena against the file catalogs for queries.
47
+
48
+ This meets requirements 1, 4, 5, 6, 7 and 11 from the user stories/requirements document,, and opens up 9 (current scope is data be findable, whereas item 9 adds searchability).
49
+
50
+ # 2. Global data registry 'HelioDataRegistry.json'
51
+
52
+ The global data registry is an unsorted json list of names and bucket endpoints. This is for buckets, not datasets (a bucket can hold multiple datasets). Queries to each endpoint can fetch the list off datasets and more information from the **endpoint**/catalog.json file.
53
+
54
+ The definitive listing of available dataset is kept in the **name for GSFC HelioCloud registry**. Providers who wish to make data visibile to the public do a one-time registration of their S3 bucket (or equivalent) to the main registry
55
+ The global data registry can be accessed directly or mirrored by anyone.
56
+
57
+ Once an S3 bucket (or equivalent) is registered in the HelioDataRegistry.json file, it does not have to be updated when new datasets are added, only when new buckets are made public.
58
+
59
+ For this global registry, only S3 buckets, not subbuckets, are allowed. For example, 's3://helio-public/' is allowed, but 's3://helio-public/MMS/' is not.
60
+
61
+ ## 2.1 Global Data Registry specification
62
+
63
+ The registry is a JSON file containing the version of this specification, the date it was last modified, and the registry list of datasets. Each dataset has two items: **endpoint** and **name**. **endpoint**s must be unique; names do not enforce uniqueness.
64
+
65
+ * **endpoint** - An accessble S3 (or equivalent) bucket link
66
+ * **name** - A descriptive name for the dataset.
67
+ * **provider** - default 'aws', indicates which cloud provider. Included for future federation.
68
+ * **region** - The AWS or equivalent region the bucket resides in
69
+
70
+ The **name** should be brief and one-line; length is not enforced by the specification but may be truncated when registration to keep things readable.
71
+
72
+ ## 2.2 Sample global data registry
73
+
74
+ Here is a sample 'HelioDataRegistry.json' for three buckets at two sites.
75
+
76
+ ```javascript
77
+ {
78
+ "version": "0.3",
79
+ "modificationDate": "2022-01-01T00:00.00Z",
80
+ "registry": [
81
+ {
82
+ "endpoint": "s3://gov-nasa-hdrl-data1/",
83
+ "name": "GSFC HelioCloud Set 1",
84
+ "provider": "aws",
85
+ "region": "us-east-1"
86
+ },
87
+ {
88
+ "endpoint": "s3://gov-nasa-hdrl-data2/",
89
+ "name": "GSFC HelioCloud Set 2",
90
+ "provider": "aws",
91
+ "region": "us-east-1"
92
+ },
93
+ {
94
+ "endpoint": "s3://edu-apl-helio-public/",
95
+ "name": "APL HelioCLoud",
96
+ "provider": "aws",
97
+ "region": "us-west-1"
98
+ }
99
+ ]
100
+ }
101
+ ```
102
+
103
+ # 3. Catalog of File Registries (per bucket catalog.json)
104
+
105
+ The catalog.json file has an entry for each dataset stored within the given S3 bucket. **endpoint** and **name** are identical to the item in the global data registry.
106
+
107
+ Globally the catalog.json describes the endpoint with the following items. Note that **endpoint** and **name** are included for clarity sake (being duplicates of the main registry) as JSON does not allow for comments, and should be the same as was provided to the global registry.
108
+
109
+ * **endpoint** - same as was provided to GlobalDataRegistry.json, an accessble S3 (or equivalent) bucket link
110
+ * **name** - same as was provided to GlobalDataRegistry.json, a descriptive name for the dataset.
111
+ * **provider** - same as was provided to GlobalDataRegistry.json, default 'aws', indicates which cloud provider. Included for future federation.
112
+ * **region** - same as was provided to GlobalDataRegistry.json, which AWS region
113
+ * **egress** - one of 'no-egress', 'user-pays', 'egress-allowed', or 'none'
114
+ * **status** - A return code, typically "1200/OK". Site owners can temporarily set this to other values
115
+ * **contact** - Who to contact for issues with this bucket, e.g. "Dr. Contact, dr_contact@example.com"
116
+
117
+ * **description** - Optional description of this collection
118
+ * **citation** - Optional how to cite, preferably a DOI for the server
119
+ * **comment** - A catch-all comment field for data provider and developer use. It should not contain information required to parse the data items.
120
+
121
+ For each dataset, the catalog entry requires:
122
+
123
+ * **id** a unique ID for the dataset that follows the ID naming requirements
124
+ * **index** a fully qualified pointer to the object directory containing both the dataset and the required cloudCatalog. It MUST start with s3:// or "https://" (or equivalent) end in a terminating '/'.
125
+ * **start**: string, Restricted ISO 8601 date/time of first record of data in the entire dataset OR the word 'static' for items such as model shapes that lack a time field.
126
+ * **stop**: string, Restricted ISO 8601 date/time of end of the last
127
+ record of data in the entire dataset OR the word 'static' for items such as model shapes that lack a time field.
128
+ * **modification**: string, Restricted ISO 8601 date/time of last time this dataset was updated
129
+ * **title** a short descriptive title sufficient to identify the dataset and its utility to users
130
+ * **indextype** Defines what format the actual cloudCatalog is, one of 'csv', 'csv-zip' or 'parquet'
131
+ * **filetype** the file format of the actual data. Must be from the prescribed list of files.
132
+
133
+ * **description** optional description for dataset".
134
+ * **resource** optional identifier e.g. SPASE ID, dataset description URL, DOI, json link of model parameters, or similar ancillary information".
135
+ * **creation** optional ISO 8601 date/time of the dataset creation".
136
+ * **expiration** optional ISO 8601 date/time after which the dataset will be expired, migrated, or not maintained".
137
+ * **verified** optional ISO 8601 date/time for when the dataset was last tested by verifier programs".
138
+ * **citation** optional how to cite this dataset, DOI or similar".
139
+ * **contact** optional contact name".
140
+ * **about** optional website URL for info, team, etc.
141
+ * **multiyear** optional True/False field (default: False) for use when dataitems span multiple years (see 3.2 below)
142
+
143
+ For file formats, there is a prescibed list. As new file formats are introduced, we will update this specification to give a single unique identifier for it. The reason for the prescribed list is to avoid ambiguity or the need for users to parse (for example, avoiding figuring out '.fts', 'fits', '.FTS', etc) Repositories with multiple files types can specify them as a comma-separated list with no spaces, e.g. 'fits,csv' for a dataset that contains both images and event lists. Currently defined types are 'fits,csv,cdf,netcdf3,netcdf4,hdf5,datamap,txt,binary,other'.
144
+
145
+ The catalog.json file has to be updated when new data is added to a dataset, by updating the **stop** item. Also, the catalog.json file is updated when a new dataset is put into that S3 bucket.
146
+
147
+ Note, currently this specification is defined around "s3://" architecture with some support for "https://" endpoints; future versions may support other protocols.
148
+
149
+ ## 3.1 ID naming requirements
150
+
151
+ The **id** field can only contain alphanumeric characters, dashes, or underscores. No spaces or other characters are allowed.
152
+
153
+ The **id** field will match the cloudCatalog files but does not have to match the sub-bucket names. The cloudCatalog includes the **id**_YYYY.csv file indices and the optional **id**.json metadata file.
154
+
155
+ ## 3.2 Data items spanning multiple years
156
+
157
+ Some data or model outputs span multiple years. An output product that covers 3 years, for example (2011-2013) would be index in "id"_2011.csv (based on the start date of the data). A time-based search that is looking for 2012 coverage would therefore not be able to find it, as no "id"_2012.csv file exists or is needed. To accommodate these edge cases, the **multiyear** field should be set to True, which will allow subsequent search layers to be aware that long-duration files exist in this dataset.
158
+
159
+ This field should not be set for the typical case where a datafile extends slightly into the next year, but only when a datafile exists to provide data for a calendar year and that datafile would not be findable based purely on its given start date.
160
+
161
+
162
+ ## 3.2 Status codes
163
+
164
+ The default status code for a catalog.json item is "1200/OK" indicating the data is available. Status codes are informative and do not enforce any limits. They exist to communicate to users and client programs if a dataset is temporarily down or has other constraints.
165
+ Other status codes defined so far include:
166
+ * "code": "1200", "message": "OK": system is up and running
167
+ * "code": "1400", "message": "temporarily unavailable": providers can set this if they temporarily are doing maintenance or need to stop access due to costs
168
+
169
+ ## 3.3 Example
170
+
171
+ Here is an example catalog, for which only the first item has decided to fill out the optional 'ownership' block. If this was the GSFC catalog.json, it would reside at s3://gov-nasa-helio-public/catalog.json.
172
+
173
+ ```javascript
174
+ {
175
+ "version": "0.3",
176
+ "endpoint": "s3://gov-nasa-helio-public/",
177
+ "name": "GSFC HelioCloud",
178
+ "provider": "aws",
179
+ "region": "us-east-1",
180
+ "egress": "no-egress",
181
+ "contact": "Dr. Contact, dr_contact@example.com",
182
+ "description": "Optional description of this collection",
183
+ "citation": "Optional how to cite, preferably a DOI for the server",
184
+ "catalog":[
185
+ {
186
+ "id": "euvml",
187
+ "index": "s3://gov-nasa-helio-public/euvml/",
188
+ "title": "EUV-ML dataset",
189
+ "start": "1995-01-01T00:00.00Z",
190
+ "stop": "2022-01-01T00:00.00Z",
191
+ "modification": "2022-01-01T00:00.00Z",
192
+ "indextype": "csv",
193
+ "filetype": "fits",
194
+ "description": "Optional description for dataset",
195
+ "resource": "optional SPASE ID, DOI, URL, or json modelset",
196
+ "creation": "optional ISO 8601 date/time of the dataset creation",
197
+ "citation": "optional how to cite this dataset, DOI or similar",
198
+ "contact": "optional contact name",
199
+ "about": "optional website URL for info, team, etc"
200
+ },
201
+ {
202
+ "id": "mms_hmi",
203
+ "index": "s3://gov-nasa-helio-public/mms/hmi/",
204
+ "title": "MMS HMI data"
205
+ "start": "2015-01-01T00:00.00Z",
206
+ "stop": "2022-01-01T00:00.00Z",
207
+ "modification": "2022-01-01T00:00.00Z",
208
+ "indextype": "csv-zip",
209
+ "filetype": "cdf"
210
+ },
211
+ {
212
+ "id": "mms_feeps",
213
+ "index": "s3://gov-nasa-helio-public/mms/feeps/",
214
+ "title": "MMS FEEPS data"
215
+ "start": "2015-01-01T00:00.00Z",
216
+ "stop": "2022-01-01T00:00.00Z",
217
+ "modification": "2022-01-01T00:00.00Z",
218
+ "indextype": "csv-zip",
219
+ "filetype": "cdf"
220
+ },
221
+ {
222
+ "id": "fluxrope",
223
+ "index": "s3://heliotest/models/",
224
+ "title": "Instatiation of 3D fluxropes"
225
+ "start": "static",
226
+ "stop": "static",
227
+ "modification": "2022-01-01T00:00.00Z",
228
+ "indextype": "csv-zip",
229
+ "filetype": "cdf"
230
+ }
231
+ ],
232
+ "status": {
233
+ "code": 1200,
234
+ "message": "OK request successful"
235
+ }
236
+ }
237
+ ```
238
+
239
+ ## 3.4 Indexes should reside in same bucket as data
240
+
241
+ Index CSV files must be in the same bucket as the data, but do not have to be in the same sub-bucket or directory as their data. The catalog points to the index files, and the index files use absolute paths to point to the data items.
242
+
243
+ This also enables design a collection of datasets, wherein the data in the actual file catalog <ID>_<YYYY>.csv files can span sub-buckets. The use of absolute file paths is mandated.
244
+
245
+ ```
246
+ Example data itself is in:
247
+ s3://example/mms1/feeps/
248
+ s3://example/mms2/feeps/
249
+ s3://example/mms3/feeps/
250
+ s3://example/mms4/feeps/
251
+ ```
252
+
253
+ ```
254
+ Case 1: Matching
255
+ file catalog locations:
256
+ s3://example/mms1/feeps/mms1_feeps.CSV
257
+ s3://example/mms2/feeps/mms2_feeps.CSV
258
+ s3://example/mms3/feeps/mms3_feeps.CSV
259
+ s3://example/mms4/feeps/mms4_feeps.CSV
260
+ ```
261
+
262
+ ```
263
+ Case 2: Not Matching
264
+ file catalog locations:
265
+ s3://example/mms_all/feeps/mms_feeps.CSV (contents point to 4 subbuckets)
266
+ ```
267
+
268
+ The first case matches the idea of a 'dataset' and MUST be provided for any provided dataset. The second matches the idea of a 'collection' and is supported but not required (i.e. additional extra cloudCatalog endpoints do not have to match the underlying data structure).
269
+
270
+ ## 3.5 Concerns
271
+
272
+ Concerns were voiced about clashes if multiple people attempt to edit the catalog.json for a bucket at the same time. Our default HelioCloud will maintain the catalog.json contents in a serverless DynamoDB (which will handle transaction collisions and provide rollback), which then outputs the 'catalog.json' file that users and data requests use.
273
+
274
+ # 4 File Catalog
275
+
276
+ The file catalog consists of one index for each year of the dataset in either csv, zipped csv, or parquet format. The file must be in time sequence and the first four items must be the **start**, **stop**, **datakey**, and **filesize** fields in that order.
277
+
278
+ The index cloudCatalog is a set of CSV or Parquet files named "index"/"id"_YYYY.csv, "index"/"id"_YYYY.csv.zip or "index"/"id"_YYYY.parquet. For the case of static non-time sequence outputs, the index cloudCatalog are named "index"/"id"_static.csv (or .csv.zip or .parquet).
279
+
280
+ ## 4.1 Required Items
281
+
282
+ * **start**: string, Restricted ISO 8601 date/time of start for that data OR the word 'static' for items such as model shapes that lack a time field
283
+ * **stop**: string, Restricted ISO 8601 date/time of stop for that data OR the word 'static' for items such as model shapes that lack a time field
284
+ * **datakey**: string, full filename or S3 object identifier sufficient to actually obtain the file
285
+ * **filesize**: integer, file size in bytes
286
+
287
+ ## 4.2 Optional Items
288
+
289
+ * **checksum**: checksum for that file. If given, **checksum_algorithm** must also be listed
290
+ * **checksum_algorithm**: checksum algorithm used if checksums are generated. Examples include SHA, others.
291
+
292
+ ## 4.3 Optional Parameters
293
+
294
+ The default expected search capability is _filetime_ within requested time range.
295
+
296
+ Anything past **filesize** is fully optional; the minimal API expects only a start time, datakey aka filehandle, and file size IN THAT ORDER in the actual file index. It is up to individual client programs to do anything past that.
297
+
298
+ Any metadata included in this per-file index must be defined in the <id>.info json file to allow parseability.
299
+
300
+ The csv or zipped csv files may or may not include a one-line header that is prefaced by "#". It is the responsibility of client programs to determine if there is a skippable header or not.
301
+
302
+ Any optional parameters from the above list or the info JSON must be in the same order specified in the JSON.
303
+
304
+ For 'static' items, since the 'start' field will be the same constant value 'static' for all items, optional parameters to distinguish the items are suggested.
305
+
306
+ ## 4.4 Accessing
307
+
308
+ Users have 3 options with the cloudCatalog CSV file:
309
+ * download it directly and parse yourself,
310
+ * use our Python API (provided) to extract a subset of filehandles from CSV,
311
+ * use AWS Athena for queries
312
+
313
+ ## 4.5 Justification for Yearly CSV/Parquet Files
314
+
315
+ Unlike a database, yearly index files are both fetchable and parseable. They have a lower cost profile than the equivalent database, reasonably fast access, and allow for client and search programs independent of a specific database implementation.
316
+
317
+ Using yearly files rather than a single file or a database is to maintain an inexpensive, stateless, easily updated file index. In AWS, the "S3 Inventory" command can generate a list of filenames and datakeys, or filenames and datakeys since the last time inventory was run. Being able to add to the catalog index files incrementally is easier served if they are chunked into yearly files.
318
+
319
+ In addition, many use cases for long time baseline datasets will not need to access the entire multi-decadal span of the data, so parsing into years reduces the downloads needed to obtain the indices.
320
+
321
+ The use of CSV or Parquet also enables AWS Athena searches within the index with little overhead, so long as optional metadata is provided.
322
+
323
+ ## 4.6 Example File Catalog
324
+
325
+ Here is a short minimal CSV example index file.
326
+
327
+ ```
328
+ # start, stop, datakey, filesize
329
+ '2010-05-08T12:05:30.000Z','2010-05-08T12:06:14.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_120530_n4euA.fts','246000'
330
+ '2010-05-08T12:06:15.000Z','2010-05-08T12:10:29.00Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_120615_n4euA.fts','246000'
331
+ '2010-05-08T12:10:30.000Z','2010-05-08T12:14:29.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_121030_n4euA.fts','246000'
332
+ ```
333
+
334
+ Here is an example with additional metadata and a CSV header as well.
335
+
336
+ ```
337
+ # start, stop, datakey, filesize, wavelength, carr_lon, carr_lat
338
+ '2010-05-08T12:05:30.000Z','2010-05-08T12:06:14.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_120530_n4euA.fts','246000','195','20.4','30.0'
339
+ '2010-05-08T12:06:15.000Z','2010-05-08T12:10:29.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_120615_n4euA.fts','246000','195','21.8','30.0'
340
+ '2010-05-08T12:10:30.000Z','2010-05-08T12:14:29.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_121030_n4euA.fts','246000','195','22.4','30.0'
341
+ ```
342
+ Here is an example with additional metadata and a CSV header as the EUV-ML project would like. Items in CAPS are directly from FITS keywords.
343
+ ```
344
+ # start, stop, datakey, filesize, spacecraft, instrument, WAVELNTH, CRLT_OBS, CRLN_OBS, CRPIX1, CRPIX2, RSUN, quality, generation_flag
345
+ '2010-05-08T12:05:30.000Z','2010-05-08T12:06:14.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_120530_n4euA.fts','246000','A','euvi','195,45.0, 23.1, 512, 510, 26.5, 1, 1
346
+ '2010-05-08T12:06:15.000Z','2010-05-08T12:10:29.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_120615_n4euA.fts','246000','A','euvi','195',45.0, 23.1, 512, 510, 26.5, 1, 1
347
+ '2010-05-08T12:10:30.000Z','2010-05-08T12:14:29.000Z','s3://edu-apl-helio-public/euvml/stereo/a/195/20100508_121030_n4euA.fts','246000','A','euvi','195',45.0, 23.1, 512, 510, 26.5, 1, 1
348
+ ```
349
+
350
+ # 5 Time Specification and ISO 8601
351
+
352
+ Time values are always strings, and the SCR Time format (taken from the HAPI Time format) is a subset of the ISO 8601 standard. The restriction on the ISO 8601 standard is that time must be represented as
353
+
354
+ ```
355
+ yyyy-mm-ddThh:mm:ss.sssZ
356
+ ```
357
+
358
+ and the trailing Z is required. Strings with less precision are allowed as per ISO 8601. Any date or time elements missing from the string are assumed to take on their smallest possible value. For example, the string 2017-01-15T23:00:00.000Z could be given in truncated form as 2017-01-15T23:00Z. A dataset must use only one format and length within that given dataset. The times values must not have any local time zone offset, and they must indicate this by including the trailing Z.
359
+
360
+
361
+ # 6 Info Metadata
362
+
363
+ Each dataset may also include an optional info json file that gives the time range, date last modified, ownership information, and optional additional metadata for that dataset. The files are by default searchable and selectable by time window. Additional search capability is not within scope of the file catalog per se, but data providers can indicate metadata for adding a search layer.
364
+
365
+ If an <id>.info file exists and lists additional parameters, the resulting catalog index files must contain those parameters in the same order as expressed in this json file.
366
+
367
+ ## 6.1 Optional Items
368
+
369
+ * **parameters**: optional list of searchable parameters available in the actual catalog index files
370
+ file.
371
+ (* = available from S3 Inventory)
372
+
373
+ ## 6.2 Example
374
+
375
+ Here is an example for a sample optional Info json file. This is used to indicate additional searchable metadata that exists within the catalog index files, and enables higher searchability in datasets.
376
+
377
+ ```javascript
378
+ {
379
+ "version": "0.3",
380
+ "parameters": [
381
+ {"name": "spacecraft", "type": "string"},
382
+ {"name": "wavelength", "type": "int", "units": "Angstroms"},
383
+ {"name": "crlt", "type": "double", "units": "degrees", "desc": "Carrington latitude"},
384
+ {"name": "crln", "type": "double", "units": "degrees", "desc": "Carrington longitude"},
385
+ {"name": "rsun", "type": "double", "units": "pixels", "desc": "Size of sun in pizels"},
386
+ {"name": "crpix1", "type": "integer", "units": "pixels", "desc": "x coord of sun center"},
387
+ {"name": "crpix2", "type": "integer", "units": "pixels", "desc": "x coord of sun center"},
388
+ {"name": "quality", "type": "integer", "desc": "data quality and level of interpolation"}
389
+ ]
390
+ }
391
+ ```
392
+
393
+ ## 7.0 Changes from 0.4 to 0.5
394
+
395
+ Changed
396
+ ```
397
+ Added 'stop' as a mandatory field
398
+ ```
@@ -30,8 +30,6 @@ cat = cr.get_catalog()
30
30
  print("get_catalog:",cat)
31
31
  reg = cr.get_registry()
32
32
  print("get registry:",reg)
33
- link = cr.get_entries()
34
- print("get_entries:",link)
35
33
  url = cr.get_endpoint("HelioCloud, including SDO")
36
34
  print("get_endpoint:", url)
37
35
 
@@ -6,7 +6,7 @@ build-backend = 'setuptools.build_meta'
6
6
 
7
7
  [project]
8
8
  name = "cloudcatalog"
9
- version = "0.4"
9
+ version = "0.5"
10
10
  authors = [ {name = "Johns Hopkins University Applied Physics Laboratory LLC", email = "sandy.antunes@jhuapl.edu"}]
11
11
  license = {text = "MIT License"}
12
12
  description = "API for accessing the generalized Cloud Catalog (cloudcatalog) specification for sharing data in and across clouds"
@@ -25,5 +25,5 @@ dependencies = [
25
25
 
26
26
  [project.urls]
27
27
  Homepage = "https://heliocloud.org"
28
- Documentation = "https://heliocloud.org"
29
- Repository = "https://gitlab.smce.nasa.gov"
28
+ Documentation = "https://github.com/heliocloud-data/cloudcatalog"
29
+ Repository = "https://github.com/heliocloud-data/cloudcatalog"
@@ -1,20 +1,19 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudcatalog
3
- Version: 0.4
3
+ Version: 0.5
4
4
  Summary: API for accessing the generalized Cloud Catalog (cloudcatalog) specification for sharing data in and across clouds
5
5
  Author-email: Johns Hopkins University Applied Physics Laboratory LLC <sandy.antunes@jhuapl.edu>
6
6
  License: MIT License
7
7
  Project-URL: Homepage, https://heliocloud.org
8
- Project-URL: Documentation, https://heliocloud.org
9
- Project-URL: Repository, https://gitlab.smce.nasa.gov
8
+ Project-URL: Documentation, https://github.com/heliocloud-data/cloudcatalog
9
+ Project-URL: Repository, https://github.com/heliocloud-data/cloudcatalog
10
10
  Keywords: cloud,index,catalog,AWS
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Topic :: Utilities
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
- License-File: LICENSE
17
- License-File: LICENSE.txt
16
+ License-File: LICENSE.MD
18
17
  Requires-Dist: boto3
19
18
  Requires-Dist: pandas
20
19
 
@@ -66,17 +65,17 @@ print(fr.get_entries())
66
65
  # also save the downloaded file index
67
66
  fr_id = 'a_dataset_id_from_the_catalog'
68
67
  start_date = '2007-02-01T00:00:00Z' # A ISO 8601 standard time and a valid time witin the mission/file-index
69
- end_date = None # A ISO 8601 standard time or None if want all the file indices after start_date
68
+ stop_date = None # A ISO 8601 standard time or None if want all the file indices after start_date
70
69
  myfiles = fr.request_cloud_catalog(fr_id, start_date=start_date, end_date=end_date, overwrite=False)
71
70
  ```
72
71
 
73
72
  ### Streaming Data from the File Catalog
74
- You now have a pandas DataFrame with startdate, key, and filesize for all the files of the mission within your specified start and end dates. From here, you can use the key to stream some of the data through EC2, a Lambda, or other processing methods.
73
+ You now have a pandas DataFrame with startdate, stopdate, key, and filesize for all the files of the mission within your specified start and end dates. From here, you can use the key to stream some of the data through EC2, a Lambda, or other processing methods.
75
74
 
76
75
  This tool also offers a simple function for streaming the data once the file catalog is obtained:
77
76
 
78
77
  ```python
79
- cloudcatalog.CloudCatalog.stream(cloud_catalog, lambda bfile, startdate, filesize: print(len(bo.read()), filesize))
78
+ cloudcatalog.CloudCatalog.stream(cloud_catalog, lambda bfile, startdate, stopdate, filesize: print(len(bo.read()), filesize))
80
79
  ```
81
80
 
82
81
  ### Searching the Entire Catalog
@@ -1,19 +1,22 @@
1
1
  .gitignore
2
- LICENSE
3
- LICENSE.txt
2
+ LICENSE.MD
4
3
  README.md
5
4
  TODO.md
6
5
  pyproject.toml
6
+ .github/bug_report.md
7
+ .github/feature_request.md
7
8
  docs/Makefile
8
9
  docs/Notes.md
9
- docs/cloudcatalog-spec-04.md
10
+ docs/cloudcatalog-spec-05.md
10
11
  docs/cloudcatalog_demo.py
11
12
  docs/conf.py
12
13
  docs/index.rst
13
14
  docs/make.bat
14
15
  docs/scr_logo.png
16
+ docs/earlier/cloudcatalog-spec-04.md
15
17
  src/__init__.py
16
18
  src/cloudcatalog.py
19
+ src/quickfail.py
17
20
  src/cloudcatalog.egg-info/PKG-INFO
18
21
  src/cloudcatalog.egg-info/SOURCES.txt
19
22
  src/cloudcatalog.egg-info/dependency_links.txt
@@ -0,0 +1,5 @@
1
+ __init__
2
+ cloudcatalog
3
+ data
4
+ helio-public_cache
5
+ quickfail
@@ -10,7 +10,110 @@ import logging
10
10
  import dateutil
11
11
  import re
12
12
  from typing import List, Dict, Tuple, Union, Optional, Callable
13
+ from botocore import UNSIGNED
14
+ from botocore.client import Config
13
15
 
16
+ # Added handler that first tries S3 anonymous, then tries https/egreess
17
+ def s3url_to_https(s3url):
18
+ """ Formula is s3://BUCKET/KEY -> https://BUCKET.s3.amazonaws.com/KEY
19
+ """
20
+ mybucket, mykey = s3url_to_bucketkey(s3url)
21
+ url = "https://" + mybucket + ".s3.amazonaws.com/" + mykey
22
+ return url
23
+
24
+ def s3url_to_bucketkey(s3url):
25
+ """
26
+ Extracts the S3 bucket name and file key from an S3 URL.
27
+
28
+ S3 paths are weird, bucket + everything else, e.g.
29
+ s3://b1/b2/b3/t.txt would be bucket b1, file b2/b3/t.txt
30
+
31
+ :param s3url: The S3 URL to extract the bucket name and file key from.
32
+
33
+ :returns: A tuple containing the S3 bucket name and file key.
34
+ """
35
+ # S3 paths are weird, bucket + everything else, e.g.
36
+ # s3://b1/b2/b3/t.txt would be bucket b1, file b2/b3/t.txt
37
+ name2 = re.sub(r"s3://", "", s3url)
38
+ s = name2.split("/", 1)
39
+ mybucket = s[0]
40
+ myfilekey = s[1] if len(s) > 1 else "" # Want None if no key?
41
+ return mybucket, myfilekey
42
+
43
+ def fetch_S3(s3url,unsigned=True,region=None,rawbytes=False, **client_kwargs):
44
+ # default is JSON, but can return raw bytes
45
+ #print("Trying S3, unsigned=",unsigned,"region=",region)
46
+ mybucket, mykey = s3url_to_bucketkey(s3url)
47
+ #print("Looking for: ",mybucket,mykey)
48
+ if unsigned:
49
+ if region != None:
50
+ s3_client = boto3.client("s3", config=Config(signature_version=UNSIGNED),region=region, **client_kwargs)
51
+ else:
52
+ s3_client = boto3.client("s3", config=Config(signature_version=UNSIGNED), **client_kwargs)
53
+ else:
54
+ if region != None:
55
+ s3_client = boto3.client("s3",region=region, **client_kwargs)
56
+ else:
57
+ s3_client = boto3.client("s3", **client_kwargs)
58
+
59
+ response = s3_client.get_object(Bucket=mybucket, Key=mykey)
60
+ status = response.get("ResponseMetadata", {}).get("HTTPStatusCode")
61
+ #print(" Success S3 unsigned",status)
62
+ if "Body" in response and status == 200:
63
+ catalog_bytes = response["Body"].read()
64
+ if rawbytes:
65
+ catalog = catalog_bytes
66
+ else:
67
+ catalog = json.loads(catalog_bytes)
68
+ elif status != 200:
69
+ print("Error, status = ",status)
70
+ return status, catalog
71
+
72
+ def fetch_url(s3url,rawbytes=False):
73
+ # default is JSON, but can return raw bytes
74
+ httpurl = s3url_to_https(s3url)
75
+ response = requests.get(httpurl)
76
+ status = response.status_code
77
+ if rawbytes:
78
+ catalog = response.content
79
+ else:
80
+ catalog = response.json()
81
+ return status, catalog
82
+
83
+ def fetch_S3orURL(s3url,region='us-east-1',rawbytes=False, **client_kwargs):
84
+ """ To get around vagualities of S3 access, this tries a cascade of:
85
+ straight fetch of S3 using your existing permissions
86
+ fetch S3 unsigned/anonymous
87
+ fetch S3 for a specified region only, defaulting to us-east-1
88
+ fetch the S3 contents via the AWS-equivalent URL
89
+ """
90
+
91
+ try:
92
+ #print("Calling unsigned")
93
+ status, catalog = fetch_S3(s3url,unsigned=True,rawbytes=rawbytes, **client_kwargs)
94
+ except:
95
+ try:
96
+ #print("Calling signed")
97
+ status, catalog = fetch_S3(s3url,unsigned=False,rawbytes=rawbytes, **client_kwargs)
98
+ except:
99
+ try:
100
+ #print("Calling region")
101
+ status, catalog = fetch_S3(s3url, unsigned=True, region=region, rawbytes=rawbytes, **client_kwargs)
102
+ except:
103
+ try:
104
+ #print("Calling url")
105
+ status, catalog = fetch_url(s3url,rawbytes=rawbytes)
106
+ except:
107
+ #print("Cannot fetch catalog, exiting.")
108
+ return None
109
+ if rawbytes:
110
+ fr_bytes_file = BytesIO()
111
+ fr_bytes_file.write(catalog)
112
+ fr_bytes_file.seek(0)
113
+ return fr_bytes_file
114
+ else:
115
+ return catalog
116
+
14
117
 
15
118
  class CatalogRegistry:
16
119
  """Use to work with the the global catalog (catalog of catalogs)."""
@@ -172,6 +275,8 @@ class CloudCatalog:
172
275
 
173
276
  self.cache = cache
174
277
 
278
+ self.catalog = fetch_S3orURL(self.bucket_name+"/catalog.json",**client_kwargs)
279
+ """ # original version, added https mod
175
280
  # Create a client object with provided kwargs
176
281
  self.s3_client = boto3.client("s3", **client_kwargs)
177
282
 
@@ -189,7 +294,9 @@ class CloudCatalog:
189
294
 
190
295
  # Load the content from json
191
296
  self.catalog = json.loads(catalog_bytes)
192
-
297
+ """
298
+
299
+
193
300
  # Check catalog format assumptions
194
301
  if any([key not in self.catalog for key in ["status", "catalog"]]):
195
302
  raise KeyError(
@@ -231,8 +338,11 @@ class CloudCatalog:
231
338
  os.mkdir(self.cache_folder)
232
339
 
233
340
  # Copy the content of the catalog to this file (overwrites)
234
- with open(os.path.join(cache_folder, "catalog.json"), "wb") as file:
235
- file.write(catalog_bytes)
341
+
342
+ #with open(os.path.join(cache_folder, "catalog.json"), "wb") as file:
343
+ # file.write(catalog_bytes)
344
+ with open(os.path.join(cache_folder, "catalog.json"), "w") as file:
345
+ json.dump(self.catalog,file, indent=4, ensure_ascii=False)
236
346
 
237
347
  def get_catalog(self) -> Dict:
238
348
  """
@@ -402,6 +512,9 @@ class CloudCatalog:
402
512
  ):
403
513
  # May through some errors, NoSuchBucket, ClientError (file may not exists or access denied)
404
514
  # If have ListBucket perms, no such key error will be raised instead of client error
515
+
516
+ fr_bytes_file = fetch_S3orURL(self.bucket_name+"/"+loc+filename,rawbytes=True)
517
+ """ # original
405
518
  response = self.s3_client.get_object(Bucket=bucket_name, Key=loc + filename)
406
519
  status = response.get("ResponseMetadata", {}).get("HTTPStatusCode")
407
520
  if "Body" in response and status == 200:
@@ -412,7 +525,8 @@ class CloudCatalog:
412
525
  raise FailedS3Get(
413
526
  f"Failed to get a cloud catalog object. Status: {stats}. Response: {response}"
414
527
  )
415
-
528
+ """
529
+
416
530
  if filepath is not None:
417
531
  with open(filepath, "wb") as file:
418
532
  file.write(fr_bytes_file.read())
@@ -423,6 +537,13 @@ class CloudCatalog:
423
537
  else:
424
538
  fr = pd.read_csv(filepath)
425
539
 
540
+ #print("Debug, version is ",self.catalog["Cloudy"])
541
+ if float(self.catalog["Cloudy"]) < 0.5:
542
+ # spec before 0.5 was start/key/filesize
543
+ # generate a 'maybe' stop using start time of prior entry
544
+ col0 = fr.columns[0]
545
+ fr.insert(1,"stop",fr[col0].shift(-1))
546
+
426
547
  # Handle # if used for the header
427
548
  if fr.columns.values[0][:2] == "# ":
428
549
  fr.columns.values[0] = fr.columns.values[0][2:]
@@ -433,15 +554,17 @@ class CloudCatalog:
433
554
  inplace=True,
434
555
  )
435
556
 
436
- # assume first column is start, second is key, and third is filesize
557
+ # assume first column is start, second is stop, third is key, and fourth is filesize
437
558
  # only assuming if not found in column names
438
559
  # no error will be thrown if one of these missing, but per spec they are required
439
560
  if "start" not in fr.columns.values:
440
561
  fr.columns.values[0] = "start"
562
+ if "stop" not in fr.columns.values:
563
+ fr.columns.values[1] = "stop"
441
564
  if "datakey" not in fr.columns.values:
442
- fr.columns.values[1] = "datakey"
565
+ fr.columns.values[2] = "datakey"
443
566
  if "filesize" not in fr.columns.values:
444
- fr.columns.values[2] = "filesize"
567
+ fr.columns.values[3] = "filesize"
445
568
 
446
569
  frs.append(fr)
447
570
 
@@ -449,14 +572,18 @@ class CloudCatalog:
449
572
 
450
573
  # Filter catalog dataframe to exact requested dates
451
574
  frs["start"] = pd.to_datetime(frs["start"], format="%Y-%m-%dT%H:%M:%SZ")
452
- frs = frs[(start_date <= frs["start"]) & (frs["start"] < stop_date)]
575
+ frs["stop"] = pd.to_datetime(frs["stop"], format="%Y-%m-%dT%H:%M:%SZ")
576
+ # mod to add files that span a time interval longer than the requested interval
577
+ # was 'start date <= file_start & file_start < stop date'
578
+ # now 'start_date <= file_start & either file_start < stop date | file_end > start_date'
579
+ frs = frs[(start_date <= frs["start"]) & (frs["start"] < stop_date) | (frs["stop"] > start_date)]
453
580
 
454
581
  return frs
455
582
 
456
583
  @staticmethod
457
584
  def stream(
458
585
  cloud_catalog: pd.DataFrame,
459
- process_func: Callable[[BytesIO, str, int], None],
586
+ process_func: Callable[[BytesIO, str, str, int], None],
460
587
  ignore_faileds3get: bool = False,
461
588
  ) -> None:
462
589
  """
@@ -465,17 +592,21 @@ class CloudCatalog:
465
592
  Parameters:
466
593
  cloud_catalog (pd.DataFrame): A pandas DataFrame containing the dataset catalog information.
467
594
  process_func (Callable): A function that takes a BytesIO object, a string representing the
468
- start date of the file, and an integer representing the file size
469
- as arguments.
595
+ start date of the file, a string representing the stop date of the file,
596
+ and an integer representing the file size as arguments.
470
597
  ignore_faileds3get (bool): A boolean that determines if the FailedS3Get is not thrown.
471
598
  """
472
- s3_client = boto3.client("s3")
473
599
 
600
+ # original version, added https mod
601
+ #s3_client = boto3.client("s3")
602
+
603
+
474
604
  fr_bytes_file = None
475
605
  for _, row in cloud_catalog.iterrows():
476
606
  # Get the S3 URL from the key in the dataframe
477
607
  s3_url = row["datakey"]
478
608
 
609
+ """ # original version, added https mod
479
610
  # Download the S3 file and read it into a BytesIO object
480
611
  response = s3_client.get_object(
481
612
  Bucket=s3_url.split("/")[2], Key="/".join(s3_url.split("/")[3:])
@@ -489,14 +620,15 @@ class CloudCatalog:
489
620
  raise FailedS3Get(
490
621
  f"Failed to get a cloud catalog object. Status: {stats}. Response: {response}"
491
622
  )
492
-
623
+ """
624
+ fr_bytes_file = fetch_S3orURL(s3_url,rawbytes=True)
493
625
  # Pass the BytesIO object, start date, and file size to the processing function
494
626
  # start may be a date object so making a string just in case for consistency
495
- process_func(fr_bytes_file, str(row["start"]), row["filesize"])
627
+ process_func(fr_bytes_file, str(row["start"]), str(row["stop"]), row["filesize"])
496
628
 
497
629
  @staticmethod
498
630
  def stream_uri(
499
- cloud_catalog: pd.DataFrame, process_func: Callable[[str, str, int], None]
631
+ cloud_catalog: pd.DataFrame, process_func: Callable[[str, str, str, int], None]
500
632
  ) -> None:
501
633
  """
502
634
  Sends S3 URLs to a processing function.
@@ -504,7 +636,8 @@ class CloudCatalog:
504
636
  Parameters:
505
637
  cloud_catalog (pd.DataFrame): A pandas DataFrame containing the dataset catalog information.
506
638
  process_func (Callable): A function that takes a string representing the S3 URL, a string
507
- representing the start date of the file, and an integer representing
639
+ representing the start date of the file, a string representing the
640
+ stop date of the file, and an integer representing
508
641
  the file size as arguments.
509
642
  """
510
643
  for _, row in cloud_catalog.iterrows():
@@ -513,7 +646,7 @@ class CloudCatalog:
513
646
 
514
647
  # Pass the S3 URL, start date, and file size to the processing function
515
648
  # start may be a date object so making a string just in case for consistency
516
- process_func(s3_url, str(row["start"]), row["filesize"])
649
+ process_func(s3_url, str(row["start"]), str(row["stop"]), row["filesize"])
517
650
 
518
651
 
519
652
  class EntireCatalogSearch:
@@ -0,0 +1,9 @@
1
+ import cloudcatalog
2
+
3
+ cr = cloudcatalog.CatalogRegistry()
4
+ endpoint = cr.get_endpoint('GSFC HelioCloud Public Temp')
5
+ fr = cloudcatalog.CloudCatalog(endpoint, cache=False)
6
+ bad="mms1_fpi_fast_des-dist"
7
+ s='2020-01-01T00:00'
8
+ e='2020-01-02T00:00'
9
+ keyset = fr.request_cloud_catalog(bad,start_date=s,stop_date=e)
cloudcatalog-0.4/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 heliocloud-data
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,4 +0,0 @@
1
- __init__
2
- cloudcatalog
3
- data
4
- gov-nasa-hdrl-data1_cache
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes