envrihub 0.0.1__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.
envrihub-0.0.1/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ MIT No Attribution
2
+
3
+ Copyright 2024 Dario De Nart
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.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18
+ SOFTWARE.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.1
2
+ Name: envrihub
3
+ Version: 0.0.1
4
+ Summary: Python library to streamline interaction with the ENVRI-Hub APIs, providing a pythonic facade to data and service access.
5
+ Home-page: https://gitlab.a.incd.pt/envri-hub-next/vre-lib
6
+ Author: ENVRI Community
7
+ Author-email: envri-hub-next-wp13-14@mailman.egi.eu
8
+ License: MIT
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+
12
+ # ENVRI Hub's VRE Library
13
+ This is the official ENVRI-Hub Python library, its purpose is to streamline interaction with the ENVRI-Hub APIs, providing a pythonic facade to data and service access.
14
+
15
+ # Quickstart
16
+ After installing the package with a quick
17
+ ```
18
+ pip install envrihub
19
+ ```
20
+ You can start using the ENVRI-HUB resorces right away through the *Hub* object:
21
+ ```
22
+ from envrihub import Hub
23
+
24
+ hub = Hub()
25
+ ```
26
+ You can query it to retrieve resources that match your needs:
27
+
28
+ ```
29
+ for res in hub.search_catalogue('bacon'):
30
+ print(res.title)
31
+ ```
32
+ You can specify free text queries, time boundaries, geographic boundaries, dara providers and/or variables you expect in your data.
33
+ Here is an example of geographical search with a [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) polygon:
34
+
35
+ ```
36
+ geography = 'POLYGON((10.70 48.34,
37
+ 28.98 48.34
38
+ 28.98 36.17,
39
+ 10.70 36.17,
40
+ 10.70 48.34))'
41
+
42
+ for i in hub.search_catalogue(geography = geography):
43
+ print(i.title)
44
+ ```
45
+
46
+ Just type `help(hub.seach_catalogue)` for the full details.
47
+
48
+ You can also access a resurce directly if you know its unique *identifier* in the Catalogue of Services:
49
+ ```
50
+ res = hub.fetch_from_catalogue('b646c445-57b8-4553-bf2f-12448ee16b55')
51
+ ```
52
+ Retrieved resources have the following properties:
53
+ + `title`: a human readable title for the resource;
54
+ + `id`: the resource's identifier in the Catalogue of Services;
55
+ + `description`: a human readable description of the resource;
56
+ + `metadata`: the whole EPOS-DCAT-AP metadata of the resource;
57
+ + `dao`: the *data access object* that allows you to get the actual data. All DAOs have an `access` method.
58
+
59
+
60
+ DAO objects are auto-generated according to the resource's metadata and can have additional methods to access data, when in doubt check them out with the `help` function:
61
+ ```
62
+ help(res.dao)
63
+ ```
64
+ If the resource is a Web Service, the DAO object allows to query such a service with all due parameters:
65
+ ```
66
+ res = hub.fetch_from_catalogue('16c3ae2f-ba39-4239-964d-12e67c378fef')
67
+ res.dao.access(id='138228')
68
+ ```
69
+ Or if the resource is a static file it lets you download it either as a bytes object or directly to your file system.
70
+ ```
71
+ res = hub.fetch_from_catalogue('b646c445-57b8-4553-bf2f-12448ee16b55')
72
+ byte_stream = res.dao.access() # bytes object
73
+ res.dao.download('path-to.file') # local file download
74
+ ```
75
+ Digital kleptomaniacs rejoice! This means that with a handful of lines you can now scrape the whole ENVRI-HUB!
76
+ ```
77
+ for res in hub.search_catalogue():
78
+ if res.is_downloadable():
79
+ res.dao.download(res.id)
80
+ ```
81
+
82
+ # Contributing
83
+ To contribute, you have to attend *ENVRI-Hub Next*'s WP13 monthly meetings. For now, if we never saw you, your pull requests will be rejected.
84
+
85
+ # Acknowledgements
86
+ This project is funded by the [ENVRI-Hub Next project](https://envri.eu/envri-hub-next/). The project received funding from the European Union’s Horizon Europe research and innovation programme under grant agreement No 101131141.
@@ -0,0 +1,75 @@
1
+ # ENVRI Hub's VRE Library
2
+ This is the official ENVRI-Hub Python library, its purpose is to streamline interaction with the ENVRI-Hub APIs, providing a pythonic facade to data and service access.
3
+
4
+ # Quickstart
5
+ After installing the package with a quick
6
+ ```
7
+ pip install envrihub
8
+ ```
9
+ You can start using the ENVRI-HUB resorces right away through the *Hub* object:
10
+ ```
11
+ from envrihub import Hub
12
+
13
+ hub = Hub()
14
+ ```
15
+ You can query it to retrieve resources that match your needs:
16
+
17
+ ```
18
+ for res in hub.search_catalogue('bacon'):
19
+ print(res.title)
20
+ ```
21
+ You can specify free text queries, time boundaries, geographic boundaries, dara providers and/or variables you expect in your data.
22
+ Here is an example of geographical search with a [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) polygon:
23
+
24
+ ```
25
+ geography = 'POLYGON((10.70 48.34,
26
+ 28.98 48.34
27
+ 28.98 36.17,
28
+ 10.70 36.17,
29
+ 10.70 48.34))'
30
+
31
+ for i in hub.search_catalogue(geography = geography):
32
+ print(i.title)
33
+ ```
34
+
35
+ Just type `help(hub.seach_catalogue)` for the full details.
36
+
37
+ You can also access a resurce directly if you know its unique *identifier* in the Catalogue of Services:
38
+ ```
39
+ res = hub.fetch_from_catalogue('b646c445-57b8-4553-bf2f-12448ee16b55')
40
+ ```
41
+ Retrieved resources have the following properties:
42
+ + `title`: a human readable title for the resource;
43
+ + `id`: the resource's identifier in the Catalogue of Services;
44
+ + `description`: a human readable description of the resource;
45
+ + `metadata`: the whole EPOS-DCAT-AP metadata of the resource;
46
+ + `dao`: the *data access object* that allows you to get the actual data. All DAOs have an `access` method.
47
+
48
+
49
+ DAO objects are auto-generated according to the resource's metadata and can have additional methods to access data, when in doubt check them out with the `help` function:
50
+ ```
51
+ help(res.dao)
52
+ ```
53
+ If the resource is a Web Service, the DAO object allows to query such a service with all due parameters:
54
+ ```
55
+ res = hub.fetch_from_catalogue('16c3ae2f-ba39-4239-964d-12e67c378fef')
56
+ res.dao.access(id='138228')
57
+ ```
58
+ Or if the resource is a static file it lets you download it either as a bytes object or directly to your file system.
59
+ ```
60
+ res = hub.fetch_from_catalogue('b646c445-57b8-4553-bf2f-12448ee16b55')
61
+ byte_stream = res.dao.access() # bytes object
62
+ res.dao.download('path-to.file') # local file download
63
+ ```
64
+ Digital kleptomaniacs rejoice! This means that with a handful of lines you can now scrape the whole ENVRI-HUB!
65
+ ```
66
+ for res in hub.search_catalogue():
67
+ if res.is_downloadable():
68
+ res.dao.download(res.id)
69
+ ```
70
+
71
+ # Contributing
72
+ To contribute, you have to attend *ENVRI-Hub Next*'s WP13 monthly meetings. For now, if we never saw you, your pull requests will be rejected.
73
+
74
+ # Acknowledgements
75
+ This project is funded by the [ENVRI-Hub Next project](https://envri.eu/envri-hub-next/). The project received funding from the European Union’s Horizon Europe research and innovation programme under grant agreement No 101131141.
@@ -0,0 +1,5 @@
1
+ '''the official ENVRI-HUB python package, allowing you to automate resource search and data access'''
2
+ __all__ =['Hub']
3
+
4
+ from envrihub.hub import Hub
5
+
@@ -0,0 +1,15 @@
1
+ class Resource():
2
+ def __init__(self, id):
3
+ self.id = id
4
+
5
+ class Dataset(Resource):
6
+ '''This class encapsulates datasets in the ENVRI-HUB.
7
+ it is made up of two attributes:
8
+ + Data: the actual data, either a datastructure or a data access object
9
+ + Metadata: information about the data, expressed as a dictionary-like structure
10
+ '''
11
+ def __init__(self, id):
12
+ self.id = id
13
+
14
+ class Service(Resource):
15
+ pass
@@ -0,0 +1,19 @@
1
+ import inspect
2
+
3
+
4
+ def delegates(to=None, keep=False):
5
+ "Decorator: replace `**kwargs` in signature with params from `to`"
6
+ def _f(f):
7
+ if to is None: to_f,from_f = f.__base__.__init__,f.__init__
8
+ else: to_f,from_f = to,f
9
+ sig = inspect.signature(from_f)
10
+ sigd = dict(sig.parameters)
11
+ k = sigd.pop('kwargs')
12
+ s2 = {k:v for k,v in inspect.signature(to_f).parameters.items()
13
+ if v.default != inspect.Parameter.empty and k not in sigd}
14
+ sigd.update(s2)
15
+ if keep: sigd['kwargs'] = k
16
+ from_f.__signature__ = sig.replace(parameters=sigd.values())
17
+ return f
18
+ return _f
19
+
@@ -0,0 +1,54 @@
1
+ from datetime import datetime
2
+ from collections.abc import Iterator
3
+ from envrihub.cos.catalogue import EposCatalogue
4
+ import shapely
5
+ import logging
6
+
7
+ from envrihub.cos.models import Distribution
8
+
9
+
10
+ class Hub():
11
+
12
+ def __init__(self, **kwargs):
13
+ self.catalogue = EposCatalogue(**kwargs)
14
+
15
+ def search_catalogue(self, query:str = '', start_date:datetime=None, end_date:datetime=None, geography = None, exv = None, provider=None)->Iterator[Distribution]:
16
+ '''
17
+ Parameters
18
+ ----------
19
+ query:str
20
+
21
+ start_date:datetime
22
+
23
+ end_date: datetime
24
+
25
+ geography: str | shapely.Geometry | tuple
26
+ geographcial bounds in WKT format, with WGS84 coordinates. Altenratively
27
+ also Shapely Geometry objects and tuples can be accepted
28
+
29
+ exv:str|list[str]
30
+ essential variables to lookup
31
+
32
+ provider: str
33
+
34
+ '''
35
+ # parse wkt geogrpahy into a Shape
36
+ if isinstance(geography, str):
37
+ try:
38
+ polygon=shapely.from_wkt(geography )
39
+ bbox = polygon.bounds
40
+ except:
41
+ logging.error('Invalid WKT geography')
42
+ elif isinstance(geography, shapely.Geometry):
43
+ bbox = geography.bounds
44
+ elif isinstance(geography, tuple):
45
+ bbox = geography
46
+ else:bbox = None
47
+ # here we are not using yield from becasue we may want to add some further
48
+ # processing/reshaping here in the near future...
49
+ for i in self.catalogue.search(query=query, bbox=bbox, startDate=start_date, endDate=end_date, exvs=exv, organisations=provider):
50
+ yield i
51
+
52
+ def fetch_from_catalogue(self, resource_id)->Distribution:
53
+ '''Retrieves a specific resource from the catalogue'''
54
+ return self.catalogue.retrieve_resource(resource_id)
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.1
2
+ Name: envrihub
3
+ Version: 0.0.1
4
+ Summary: Python library to streamline interaction with the ENVRI-Hub APIs, providing a pythonic facade to data and service access.
5
+ Home-page: https://gitlab.a.incd.pt/envri-hub-next/vre-lib
6
+ Author: ENVRI Community
7
+ Author-email: envri-hub-next-wp13-14@mailman.egi.eu
8
+ License: MIT
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+
12
+ # ENVRI Hub's VRE Library
13
+ This is the official ENVRI-Hub Python library, its purpose is to streamline interaction with the ENVRI-Hub APIs, providing a pythonic facade to data and service access.
14
+
15
+ # Quickstart
16
+ After installing the package with a quick
17
+ ```
18
+ pip install envrihub
19
+ ```
20
+ You can start using the ENVRI-HUB resorces right away through the *Hub* object:
21
+ ```
22
+ from envrihub import Hub
23
+
24
+ hub = Hub()
25
+ ```
26
+ You can query it to retrieve resources that match your needs:
27
+
28
+ ```
29
+ for res in hub.search_catalogue('bacon'):
30
+ print(res.title)
31
+ ```
32
+ You can specify free text queries, time boundaries, geographic boundaries, dara providers and/or variables you expect in your data.
33
+ Here is an example of geographical search with a [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) polygon:
34
+
35
+ ```
36
+ geography = 'POLYGON((10.70 48.34,
37
+ 28.98 48.34
38
+ 28.98 36.17,
39
+ 10.70 36.17,
40
+ 10.70 48.34))'
41
+
42
+ for i in hub.search_catalogue(geography = geography):
43
+ print(i.title)
44
+ ```
45
+
46
+ Just type `help(hub.seach_catalogue)` for the full details.
47
+
48
+ You can also access a resurce directly if you know its unique *identifier* in the Catalogue of Services:
49
+ ```
50
+ res = hub.fetch_from_catalogue('b646c445-57b8-4553-bf2f-12448ee16b55')
51
+ ```
52
+ Retrieved resources have the following properties:
53
+ + `title`: a human readable title for the resource;
54
+ + `id`: the resource's identifier in the Catalogue of Services;
55
+ + `description`: a human readable description of the resource;
56
+ + `metadata`: the whole EPOS-DCAT-AP metadata of the resource;
57
+ + `dao`: the *data access object* that allows you to get the actual data. All DAOs have an `access` method.
58
+
59
+
60
+ DAO objects are auto-generated according to the resource's metadata and can have additional methods to access data, when in doubt check them out with the `help` function:
61
+ ```
62
+ help(res.dao)
63
+ ```
64
+ If the resource is a Web Service, the DAO object allows to query such a service with all due parameters:
65
+ ```
66
+ res = hub.fetch_from_catalogue('16c3ae2f-ba39-4239-964d-12e67c378fef')
67
+ res.dao.access(id='138228')
68
+ ```
69
+ Or if the resource is a static file it lets you download it either as a bytes object or directly to your file system.
70
+ ```
71
+ res = hub.fetch_from_catalogue('b646c445-57b8-4553-bf2f-12448ee16b55')
72
+ byte_stream = res.dao.access() # bytes object
73
+ res.dao.download('path-to.file') # local file download
74
+ ```
75
+ Digital kleptomaniacs rejoice! This means that with a handful of lines you can now scrape the whole ENVRI-HUB!
76
+ ```
77
+ for res in hub.search_catalogue():
78
+ if res.is_downloadable():
79
+ res.dao.download(res.id)
80
+ ```
81
+
82
+ # Contributing
83
+ To contribute, you have to attend *ENVRI-Hub Next*'s WP13 monthly meetings. For now, if we never saw you, your pull requests will be rejected.
84
+
85
+ # Acknowledgements
86
+ This project is funded by the [ENVRI-Hub Next project](https://envri.eu/envri-hub-next/). The project received funding from the European Union’s Horizon Europe research and innovation programme under grant agreement No 101131141.
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ envrihub/__init__.py
5
+ envrihub/dataset.py
6
+ envrihub/decorators.py
7
+ envrihub/hub.py
8
+ envrihub.egg-info/PKG-INFO
9
+ envrihub.egg-info/SOURCES.txt
10
+ envrihub.egg-info/dependency_links.txt
11
+ envrihub.egg-info/requires.txt
12
+ envrihub.egg-info/top_level.txt
13
+ tests/test_basics.py
14
+ tests/test_cos.py
15
+ tests/test_hub.py
16
+ tests/test_openapi.py
@@ -0,0 +1,7 @@
1
+ shapely
2
+ retry
3
+ openapi3-parser
4
+ prance
5
+ pandas
6
+ argopy
7
+ SPARQLWrapper
@@ -0,0 +1 @@
1
+ envrihub
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ from setuptools import setup
2
+
3
+ with open("README.md", 'r') as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name='envrihub',
8
+ version='0.0.1',
9
+ description='Python library to streamline interaction with the ENVRI-Hub APIs, providing a pythonic facade to data and service access.',
10
+ license="MIT",
11
+ long_description=long_description,
12
+ long_description_content_type="text/markdown",
13
+ author='ENVRI Community',
14
+ author_email='envri-hub-next-wp13-14@mailman.egi.eu',
15
+ url="https://gitlab.a.incd.pt/envri-hub-next/vre-lib",
16
+ packages=['envrihub'],
17
+ install_requires=['shapely','retry','openapi3-parser','prance','pandas','argopy','SPARQLWrapper'], #external packages as dependencies
18
+ )
@@ -0,0 +1,20 @@
1
+ ## Datetime parsing
2
+ from datetime import datetime
3
+ import pytz
4
+ from envrihub.data_access.argo import to_argo_datetime
5
+ from envrihub.data_access.open_api_client import _to_parameter_name
6
+ from envrihub.data_access.seadatanet import to_seadatanet_datetime
7
+
8
+
9
+ def test_datetime_parsing_argo_outformat():
10
+ t = to_argo_datetime(datetime(2024, 1, 23, 9, 21, 54, tzinfo=pytz.utc))
11
+ target = '2024-01-23T09:21:54+00:00'
12
+ assert t==target, f'Incorrectly parsed datetime, expected {target}, but got {t}'
13
+
14
+ def test_datetime_parsing_seadatanet_outformat():
15
+ t = to_seadatanet_datetime(datetime(2024, 1, 23, 9, 21, 54, tzinfo=pytz.utc))
16
+ target = '2024-01-23'
17
+ assert t==target, f'Incorrectly parsed datetime, expected {target}, but got {t}'
18
+
19
+ def test_to_parameter_name():
20
+ assert _to_parameter_name('Content-Type') == 'Content_Type', 'Parameter names are not not sanitized enough'
@@ -0,0 +1,40 @@
1
+ from collections.abc import Iterable
2
+ from envrihub.cos.catalogue import EposCatalogue
3
+ from envrihub.cos.models import Distribution
4
+ from envrihub.data_access.models import DataAccessObject
5
+ TEST_CATALOGUE_ADDRESS ='https://catalogue.staging.envri.eu/api/v1'
6
+
7
+ def test_distribution():
8
+ cos = EposCatalogue(TEST_CATALOGUE_ADDRESS )
9
+ results = list(cos.search('beacon'))
10
+ dist = cos.retrieve_resource(results[0].id)
11
+ assert isinstance(dist, Distribution), f'{str(dist)} is not a Distribution object'
12
+
13
+ def test_distribution_dao():
14
+ cos = EposCatalogue(TEST_CATALOGUE_ADDRESS )
15
+ results = list(cos.search('beacon'))
16
+ dist = cos.retrieve_resource(results[0].id)
17
+ assert dist.id == results[0].id, f'Catalogue returned id {str(dist.id)} which is different from the requested one'
18
+
19
+ def test_dao_access():
20
+ cos = EposCatalogue(TEST_CATALOGUE_ADDRESS )
21
+ results = list(cos.search('marine species'))
22
+ dist = cos.retrieve_resource(results[0].id)
23
+ data = dist.dao.access()
24
+ assert isinstance(data, Iterable), 'Unable to parse JSON. Probably connection does not work'
25
+
26
+ def test_catalogue_search_free_text():
27
+ cos = EposCatalogue(TEST_CATALOGUE_ADDRESS )
28
+ results = list(cos.search('marine species'))
29
+ assert len(results)>1, 'Catalogue does not find sh*t with free text search'
30
+
31
+ def test_catalogue_search_coords():
32
+ cos = EposCatalogue(TEST_CATALOGUE_ADDRESS )
33
+ results = list(cos.search(bbox = (10.70, 36.17, 28.98, 48.34)))
34
+ assert len(results)>1, 'Catalogue does not find sh*t with geo search'
35
+
36
+ def test_file_access_from_cos():
37
+ cos = EposCatalogue(TEST_CATALOGUE_ADDRESS )
38
+ results = list(cos.search('beacon'))
39
+ dist = cos.retrieve_resource(results[0].id)
40
+ assert isinstance(dist.dao.access(), bytes), 'File access does not download bytes object'
@@ -0,0 +1,8 @@
1
+ '''Testing the Hub class'''
2
+
3
+ from envrihub import Hub
4
+
5
+ def test_geo_search():
6
+ geography = 'POLYGON((10.703125000000004 48.345191092562935,28.984375000000004 48.345191092562935,28.984375000000004 36.17766212248528,10.703125000000004 36.17766212248528,10.703125000000004 48.345191092562935))'
7
+ hub = Hub()
8
+ assert len(list(hub.search_catalogue(geography = geography)))>0, 'Geographic search from the Hub does not work'
@@ -0,0 +1,27 @@
1
+ import envrihub
2
+ from envrihub.data_access.models import DataAccessObject, OpenAPI3DataAccess
3
+ from envrihub.data_access.open_api_client import build_access_class, recursion_limit_handler_void
4
+ from prance import ResolvingParser
5
+
6
+
7
+ def test_recursive_spec_parsing():
8
+ parser = ResolvingParser('https://catalogue.staging.envri.eu/api/v1/openapi.json', recursion_limit_handler=recursion_limit_handler_void)
9
+ parser.parse()
10
+ specification = parser.specification
11
+ assert isinstance(specification, dict), f'Parsed specfication is {type(specification)} instead of good ol dict'
12
+
13
+ def test_openapi_client():
14
+ parser = ResolvingParser('https://catalogue.staging.envri.eu/api/v1/openapi.json', recursion_limit_handler=recursion_limit_handler_void)
15
+ parser.parse()
16
+ specification = parser.specification
17
+ dao = build_access_class(specification, base_address='https://catalogue.staging.envri.eu/')()
18
+ assert isinstance(dao, OpenAPI3DataAccess), 'Wrong DAO type found'
19
+
20
+ def test_openapi_client_call():
21
+ parser = ResolvingParser('https://catalogue.staging.envri.eu/api/v1/openapi.json', recursion_limit_handler=recursion_limit_handler_void)
22
+ parser.parse()
23
+ specification = parser.specification
24
+ dao = build_access_class(specification, base_address='https://catalogue.staging.envri.eu/')
25
+ dao_instance = dao()
26
+ result = dao_instance.resourcessearch(q='minchia')
27
+ assert isinstance(result, dict), 'OpenAPI client does not manage to make a proper GET request'