deep-code 0.0.1.dev0__py3-none-any.whl

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.
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # Copyright (c) 2025 by Brockmann Consult GmbH
4
+ # Permissions are hereby granted under the terms of the MIT License:
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ from typing import Literal
8
+
9
+ import pystac
10
+ from pystac import Extent, SpatialExtent, TemporalExtent
11
+ from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
12
+
13
+ from deep_code.constants import CF_SCHEMA_URI, OSC_SCHEMA_URI
14
+
15
+
16
+ class OscExtension(
17
+ PropertiesExtension, ExtensionManagementMixin[pystac.Item | pystac.Collection]
18
+ ):
19
+ """Handles the OSC extension for STAC Items and Collections.
20
+
21
+ Args:
22
+ obj: The STAC Item or Collection to which the OSC extension is applied.
23
+ """
24
+
25
+ name: Literal["osc"] = "osc"
26
+
27
+ def __init__(self, obj: pystac.Item | pystac.Collection):
28
+ if isinstance(obj, pystac.Collection):
29
+ self.properties = obj.extra_fields
30
+ else:
31
+ self.properties = obj.properties
32
+ self.obj = obj
33
+
34
+ @property
35
+ def osc_type(self) -> str | None:
36
+ return self._get_property("osc:type", str)
37
+
38
+ @osc_type.setter
39
+ def osc_type(self, v: str) -> None:
40
+ self._set_property("osc:type", v, pop_if_none=False)
41
+
42
+ @property
43
+ def osc_name(self) -> str | None:
44
+ return self._get_property("osc:name", str)
45
+
46
+ @osc_name.setter
47
+ def osc_name(self, v: str) -> None:
48
+ self._set_property("osc:name", v, pop_if_none=False)
49
+
50
+ @property
51
+ def osc_status(self) -> str | None:
52
+ return self._get_property("osc:status", str)
53
+
54
+ @osc_status.setter
55
+ def osc_status(self, value: str) -> None:
56
+ self._set_property("osc:status", value, pop_if_none=False)
57
+
58
+ @property
59
+ def osc_project(self) -> str | None:
60
+ return self._get_property("osc:project", str)
61
+
62
+ @osc_project.setter
63
+ def osc_project(self, v: str) -> None:
64
+ self._set_property("osc:project", v, pop_if_none=False)
65
+
66
+ @property
67
+ def osc_themes(self) -> list[str] | None:
68
+ return self._get_property("osc:themes", list)
69
+
70
+ @osc_themes.setter
71
+ def osc_themes(self, value: list[str]) -> None:
72
+ if not isinstance(value, list) or not all(
73
+ isinstance(item, str) for item in value
74
+ ):
75
+ raise ValueError("osc:themes must be a list of strings")
76
+ self._set_property("osc:themes", value, pop_if_none=False)
77
+
78
+ @property
79
+ def osc_region(self) -> str | None:
80
+ return self._get_property("osc:region", str)
81
+
82
+ @osc_region.setter
83
+ def osc_region(self, value: str) -> None:
84
+ self._set_property("osc:region", value, pop_if_none=False)
85
+
86
+ @property
87
+ def osc_missions(self) -> list[str] | None:
88
+ return self._get_property("osc:missions", list)
89
+
90
+ @osc_missions.setter
91
+ def osc_missions(self, value: list[str]) -> None:
92
+ if not isinstance(value, list) or not all(
93
+ isinstance(item, str) for item in value
94
+ ):
95
+ raise ValueError("osc:missions must be a list of strings")
96
+ self._set_property("osc:missions", value, pop_if_none=False)
97
+
98
+ def set_extent(self, spatial: list[list[float]], temporal: list[list[str]]) -> None:
99
+ self.obj.extent = Extent(SpatialExtent(spatial), TemporalExtent(temporal))
100
+
101
+ @property
102
+ def osc_variables(self) -> list[str] | None:
103
+ return self._get_property("osc:variables", list)
104
+
105
+ @osc_variables.setter
106
+ def osc_variables(self, v: list[str]) -> None:
107
+ if not isinstance(v, list) or not all(isinstance(item, str) for item in v):
108
+ raise ValueError("osc:variables must be a list of strings")
109
+ self._set_property("osc:variables", v, pop_if_none=False)
110
+
111
+ @property
112
+ def keywords(self) -> list[str] | None:
113
+ return self._get_property("keywords", list)
114
+
115
+ @keywords.setter
116
+ def keywords(self, value: list[str]) -> None:
117
+ if not isinstance(value, list) or not all(
118
+ isinstance(item, str) for item in value
119
+ ):
120
+ raise ValueError("keywords must be a list of strings")
121
+ self._set_property("keywords", value, pop_if_none=False)
122
+
123
+ @property
124
+ def cf_parameter(self) -> list[dict] | None:
125
+ return self._get_property("cf:parameter", list)
126
+
127
+ @cf_parameter.setter
128
+ def cf_parameter(self, value: list[dict]) -> None:
129
+ if not isinstance(value, list) or not all(
130
+ isinstance(item, dict) for item in value
131
+ ):
132
+ raise ValueError("cf:parameter must be a list of dictionaries")
133
+ self._set_property("cf:parameter", value, pop_if_none=False)
134
+
135
+ @property
136
+ def created(self) -> str | None:
137
+ return self._get_property("created", str)
138
+
139
+ @created.setter
140
+ def created(self, value: str) -> None:
141
+ self._set_property("created", value, pop_if_none=False)
142
+
143
+ @property
144
+ def updated(self) -> str | None:
145
+ return self._get_property("updated", str)
146
+
147
+ @updated.setter
148
+ def updated(self, value: str) -> None:
149
+ self._set_property("updated", value, pop_if_none=False)
150
+
151
+ @classmethod
152
+ def get_schema_uri(cls) -> list[str]:
153
+ return [OSC_SCHEMA_URI, CF_SCHEMA_URI]
154
+
155
+ @classmethod
156
+ def ext(
157
+ cls, obj: pystac.Item | pystac.Collection, add_if_missing: bool = False
158
+ ) -> "OscExtension":
159
+ """Returns the OscExtension instance for the given object, adding the extension
160
+ if missing."""
161
+ if cls.has_extension(obj):
162
+ return OscExtension(obj)
163
+ elif add_if_missing:
164
+ return cls.add_to(obj)
165
+ else:
166
+ raise ValueError(
167
+ "OSC extension is not present and add_if_missing is False."
168
+ )
169
+
170
+ @classmethod
171
+ def has_extension(cls, obj: pystac.Item | pystac.Collection) -> bool:
172
+ """Checks if all required extensions are present."""
173
+ schema_uris = cls.get_schema_uri()
174
+ if isinstance(schema_uris, list):
175
+ return all(uri in obj.stac_extensions for uri in schema_uris)
176
+ elif isinstance(schema_uris, str):
177
+ return schema_uris in obj.stac_extensions
178
+
179
+ @classmethod
180
+ def add_to(cls, obj: pystac.Item | pystac.Collection) -> "OscExtension":
181
+ """Adds the OSC and CF extensions to the object's extensions."""
182
+ schema_uris = cls.get_schema_uri()
183
+ if isinstance(schema_uris, list): # Handle list of URIs
184
+ for uri in schema_uris:
185
+ if uri not in obj.stac_extensions:
186
+ obj.stac_extensions.append(uri)
187
+ elif isinstance(schema_uris, str): # Handle single URI
188
+ if schema_uris not in obj.stac_extensions:
189
+ obj.stac_extensions.append(schema_uris)
190
+ return OscExtension(obj)
191
+
192
+ def validate_extension(self) -> None:
193
+ """Validates that all required fields for the OSC extension are set."""
194
+ required_fields = ["osc:type", "osc:project", "osc:status"]
195
+ missing_fields = [
196
+ field
197
+ for field in required_fields
198
+ if self._get_property(field, None) is None
199
+ ]
200
+ if missing_fields:
201
+ raise ValueError(f"Missing required fields: {', '.join(missing_fields)}")
deep_code/version.py ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT License (MIT)
2
+ # Copyright (c) 2024 by DeepESDL and Brockmann Consult GmbH
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a
5
+ # copy of this software and associated documentation files (the "Software"),
6
+ # to deal in the Software without restriction, including without limitation
7
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ # and/or sell copies of the Software, and to permit persons to whom the
9
+ # Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
+ # DEALINGS IN THE SOFTWARE.
21
+
22
+ version = "0.0.1.dev0"
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 DeepESDL and Brockmann Consult GmbH
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.
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.2
2
+ Name: deep_code
3
+ Version: 0.0.1.dev0
4
+ Summary: deepesdl earthcode integration utility tool
5
+ Author-email: Tejas Morbagal Harish <tejas.morbagalharish@brockmann-consult.de>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/deepesdl/deep-code
8
+ Project-URL: Issues, https://github.com/deepesdl/deep-code/issues
9
+ Project-URL: Changelog, https://github.com/deepesdl/deep-code/blob/main/CHANGES.md
10
+ Keywords: analysis ready data,data science,datacube,xarray,zarr,xcube,stac,FAIR,reproducible workflow,DeepESDL
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: click
15
+ Requires-Dist: fsspec
16
+ Requires-Dist: jsonschema
17
+ Requires-Dist: requests
18
+ Requires-Dist: pandas
19
+ Requires-Dist: pystac
20
+ Requires-Dist: pyyaml
21
+ Requires-Dist: xcube-core
22
+ Requires-Dist: xrlint
23
+ Provides-Extra: dev
24
+ Requires-Dist: black; extra == "dev"
25
+ Requires-Dist: flake8; extra == "dev"
26
+ Requires-Dist: numpy; extra == "dev"
27
+ Requires-Dist: ruff; extra == "dev"
28
+ Requires-Dist: pytest; extra == "dev"
29
+ Requires-Dist: pytest-cov; extra == "dev"
30
+ Requires-Dist: pytest-recording; extra == "dev"
31
+
32
+ # deep-code
33
+
34
+ [![Build Status](https://github.com/deepesdl/deep-code/actions/workflows/unittest-workflow.yaml/badge.svg)](https://github.com/deepesdl/deep-code/actions/workflows/unittest-workflow.yaml)
35
+ [![codecov](https://codecov.io/gh/deepesdl/deep-code/graph/badge.svg?token=47MQXOXWOK)](https://codecov.io/gh/deepesdl/deep-code)
36
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
37
+ [![License](https://img.shields.io/github/license/dcs4cop/xcube-smos)](https://github.com/deepesdl/deep-code/blob/main/LICENSE)
38
+
39
+ `deep-code` is a lightweight python tool that comprises a command line interface(CLI)
40
+ and Python API providing utilities that aid integration of DeepESDL datasets,
41
+ experiments with EarthCODE.
42
+
43
+ The first release will focus on implementing the publish feature of DeepESDL
44
+ experiments/workflow as OGC API record and Datasets as an OSC stac collection.
45
+
46
+ ## Setup
47
+
48
+ ## Install
49
+ `deep-code` will be available in PyPI and conda-forge. Till the stable release,
50
+ developers/contributors can follow the below steps to install deep-code.
51
+
52
+ ## Installing from the repository for Developers/Contributors
53
+
54
+ To install deep-code directly from the git repository, clone the repository, and execute the steps below:
55
+
56
+ ```commandline
57
+ conda env create
58
+ conda activate deep-code
59
+ pip install -e .
60
+ ```
61
+
62
+ This installs all the dependencies of `deep-code` into a fresh conda environment,
63
+ and installs deep-code from the repository into the same environment.
64
+
65
+ ## Testing
66
+
67
+ To run the unit test suite:
68
+
69
+ ```commandline
70
+ pytest
71
+ ```
72
+
73
+ To analyze test coverage
74
+ ```shell
75
+ pytest --cov=deep-code
76
+ ```
77
+
78
+ To produce an HTML coverage report
79
+
80
+ ```commandline
81
+ pytest --cov-report html --cov=deep-code
82
+ ```
83
+
84
+ ## deep_code usage
85
+
86
+ `deep_code` provides a command-line tool called deep-code, which has several subcommands
87
+ providing different utility functions.
88
+ Use the --help option with these subcommands to get more details on usage.
89
+
90
+ ### deep-code publish-product
91
+
92
+ Publish a dataset which is a result of an experiment to the EarthCODE
93
+ open-science catalog.
94
+
95
+ ```commandline
96
+ deep-code publish-dataset /path/to/dataset-config.yaml
97
+ ```
98
+
99
+ #### .gitaccess example
100
+
101
+ ```
102
+ github-username: your-git-user
103
+ github-token: personal access token
104
+ ```
105
+
106
+ #### dataset-config.yaml example
107
+
108
+ ```
109
+ dataset_id: hydrology-1D-0.009deg-100x60x60-3.0.2.zarr
110
+ collection_id: hydrology
111
+ osc_themes:
112
+ - Land
113
+ - Oceans
114
+ # non-mandatory
115
+ documentation_link: https://deepesdl.readthedocs.io/en/latest/datasets/hydrology-1D-0.009deg-100x60x60-3.0.2.zarr/
116
+ access_link: s3://test
117
+ dataset_status: completed
118
+ osc_region: global
119
+ cf_parameter:
120
+ - name: hydrology
121
+ ```
122
+
123
+ dataset-id has to be a valid dataset-id from `deep-esdl-public` s3 or your team bucket.
124
+
125
+ ### deep-code publish-workflow
126
+
127
+ Publish a workflow/experiment to the EarthCODE open-science catalog.
128
+
129
+ ```commandline
130
+ deep-code publish-workflow /path/to/workflow-config.yaml
131
+ ```
132
+ #### workflow-config.yaml example
133
+
134
+ ```
135
+ workflow_id: "4D Med hydrology cube generation"
136
+ properties:
137
+ title: "Hydrology cube generation recipe"
138
+ description: "4D Med cube generation"
139
+ keywords:
140
+ - Earth Science
141
+ themes:
142
+ - Atmosphere
143
+ - Ocean
144
+ - Evaporation
145
+ license: proprietary
146
+ jupyter_kernel_info:
147
+ name: deepesdl-xcube-1.7.1
148
+ python_version: 3.11
149
+ env_file: https://git/env.yml
150
+ links:
151
+ - rel: "documentation"
152
+ type: "application/json"
153
+ title: "4DMed Hydrology Cube Generation Recipe"
154
+ href: "https://github.com/deepesdl/cube-gen/tree/main/hydrology/README.md"
155
+ - rel: "jupyter-notebook"
156
+ type: "application/json"
157
+ title: "Workflow Jupyter Notebook"
158
+ href: "https://github.com/deepesdl/cube-gen/blob/main/hydrology/notebooks/reading_hydrology.ipynb"
159
+ contact:
160
+ - name: Tejas Morbagal Harish
161
+ organization: Brockmann Consult GmbH
162
+ links:
163
+ - rel: "about"
164
+ type: "text/html"
165
+ href: "https://www.brockmann-consult.de/"
166
+ ```
@@ -0,0 +1,33 @@
1
+ deep_code/__init__.py,sha256=Ofrv0AucJxXnz3SCof3viqr5VkpLe6rMJ-TIzYu9H2k,1195
2
+ deep_code/constants.py,sha256=0sZqGuQl9Ts09yFhztYBVLjtfw-CbMxqbE04xUVvv5o,711
3
+ deep_code/version.py,sha256=-6iVJEGyKSV6z7wxI-YfBdtrQbOYfu245WWXk7s3LEc,1166
4
+ deep_code/cli/__init__.py,sha256=75_6wLIicI9tVNEwxlcU2gYOqH8_0pUvmNJI_DrmiIw,155
5
+ deep_code/cli/main.py,sha256=nCXlv4-ztInmfDVpHbVpglLlA0OWY1I0M6i42HUXhzo,435
6
+ deep_code/cli/publish.py,sha256=WUCGf8KMiwrEMogwyTA85LEj3iSEnqL-VHC0IX_FOa4,860
7
+ deep_code/tests/tools/__init__.py,sha256=75_6wLIicI9tVNEwxlcU2gYOqH8_0pUvmNJI_DrmiIw,155
8
+ deep_code/tests/tools/test_publish.py,sha256=9jkuTPuqcjVaLTWryfs28j1yzi2fR1855pQyoT2IIpk,4506
9
+ deep_code/tests/utils/__init__.py,sha256=75_6wLIicI9tVNEwxlcU2gYOqH8_0pUvmNJI_DrmiIw,155
10
+ deep_code/tests/utils/test_dataset_stac_generator.py,sha256=win6YIUzKn1mxf07EYLW_Q1WJ-ObMWQylZomScBszVs,9289
11
+ deep_code/tests/utils/test_github_automation.py,sha256=bPOVfI40TdAdlckDkJSx4Ez8mU8gqqj3MJkHsoL8O-c,4180
12
+ deep_code/tests/utils/test_ogc_api_record.py,sha256=vQV8byQq3nPt-bvf-hssCcF_LUYSqtdN9sSYS-6v_Ys,4455
13
+ deep_code/tests/utils/test_ogc_record_generator.py,sha256=uDjH-7Y6G-ku7jACxMZLhIl8W6E9D869FsMre6yhsC4,2637
14
+ deep_code/tests/utils/test_osc_extension.py,sha256=DFKns2N87P5duewVabY3zsDdKTatjT-mLBn0pvH5sOE,5020
15
+ deep_code/tools/__init__.py,sha256=75_6wLIicI9tVNEwxlcU2gYOqH8_0pUvmNJI_DrmiIw,155
16
+ deep_code/tools/check.py,sha256=4YV2dnzBf42C6n1Ttfp6WJiyiqnTW_4DY_07edYe5Yw,152
17
+ deep_code/tools/new.py,sha256=6Sp5nOunyoep8PjYqKELJjmcPboyG99bMbwPBE8Olrc,317
18
+ deep_code/tools/publish.py,sha256=ObZkBZyHIM1OXIgvedOimRrGaCfXBPw8uYQKuPdk8SY,8533
19
+ deep_code/tools/register.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ deep_code/tools/setup_ci.py,sha256=pgj3sSOTjUvocfwQm54Rj7GufupDM65YHAnrEpFc9iU,43
21
+ deep_code/tools/test.py,sha256=PkpQ4PL9H_ow7_e3sDhyftsE2OA9sJv8tlb_jiM8pgk,133
22
+ deep_code/utils/__init__.py,sha256=75_6wLIicI9tVNEwxlcU2gYOqH8_0pUvmNJI_DrmiIw,155
23
+ deep_code/utils/dataset_stac_generator.py,sha256=MXXA2Sh2RPHflG1N0I_GtuQ37pDrQK02LYKE8d6m5bU,16144
24
+ deep_code/utils/github_automation.py,sha256=ZdS7CugadazT-viSZzGlpnryCiG4Mb7HUt_dME1_98k,5002
25
+ deep_code/utils/ogc_api_record.py,sha256=2-G-I2yjMjRC1zDZbT2vI6kXiAmIQdmdUZsuwRVy9Xs,2877
26
+ deep_code/utils/ogc_record_generator.py,sha256=jCVkdiGJqvHAVMADed_gMFTPsKaRNp-v1bq8O62R75g,1976
27
+ deep_code/utils/osc_extension.py,sha256=4aJ-U-tEjMRJ_U_J9QjnuxpzaR6HH_xqwm5t8qgcBEE,7103
28
+ deep_code-0.0.1.dev0.dist-info/LICENSE,sha256=bzOmAfwEn0ijhaLp6cZEjv227VjpKNq-tkQoCqcEX68,1092
29
+ deep_code-0.0.1.dev0.dist-info/METADATA,sha256=DtBBMkrkxaz48zycF3rgrF-To6Pf53XI1ppef6trki0,5032
30
+ deep_code-0.0.1.dev0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
31
+ deep_code-0.0.1.dev0.dist-info/entry_points.txt,sha256=oazQRLS3kCF3Dd2lJXmAnlzDxPm2CzVSq1ySJPQc8qk,54
32
+ deep_code-0.0.1.dev0.dist-info/top_level.txt,sha256=jqozOicycBsaOgbwKUtZeI8OGEeOFO_7CJclanpdMpQ,10
33
+ deep_code-0.0.1.dev0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ deep-code = deep_code.cli.main:main
@@ -0,0 +1 @@
1
+ deep_code