CMIP7-data-request-api 1.1.2__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.
Files changed (36) hide show
  1. CMIP7_data_request_api-1.1.2.dist-info/LICENSE +21 -0
  2. CMIP7_data_request_api-1.1.2.dist-info/METADATA +210 -0
  3. CMIP7_data_request_api-1.1.2.dist-info/RECORD +36 -0
  4. CMIP7_data_request_api-1.1.2.dist-info/WHEEL +5 -0
  5. CMIP7_data_request_api-1.1.2.dist-info/entry_points.txt +2 -0
  6. CMIP7_data_request_api-1.1.2.dist-info/top_level.txt +1 -0
  7. data_request_api/__init__.py +1 -0
  8. data_request_api/command_line/__init__.py +0 -0
  9. data_request_api/command_line/export_dreq_lists_json.py +136 -0
  10. data_request_api/dev/JA/__init__.py +0 -0
  11. data_request_api/dev/JA/check_plev_requests.py +416 -0
  12. data_request_api/dev/JA/read_feedback_spreadsheet.py +141 -0
  13. data_request_api/dev/JA/workflow_example_GRtest.py +275 -0
  14. data_request_api/dev/JA/workflow_example_test.py +222 -0
  15. data_request_api/dev/MM/checksum.py +68 -0
  16. data_request_api/dev/MM/walking_data_request.ipynb +380 -0
  17. data_request_api/dev/MS/dreq_content_and_walking_data_request.ipynb +3755 -0
  18. data_request_api/dev/__init__.py +0 -0
  19. data_request_api/stable/__init__.py +0 -0
  20. data_request_api/stable/content/README.MD +106 -0
  21. data_request_api/stable/content/__init__.py +0 -0
  22. data_request_api/stable/content/dreq_api/__init__.py +0 -0
  23. data_request_api/stable/content/dreq_api/consolidate_export.py +488 -0
  24. data_request_api/stable/content/dreq_api/dreq_content.py +593 -0
  25. data_request_api/stable/content/dreq_api/mapping_table.py +335 -0
  26. data_request_api/stable/content/dreq_api/test_dreq_content.py +194 -0
  27. data_request_api/stable/content/dump_transformation.py +550 -0
  28. data_request_api/stable/query/__init__.py +0 -0
  29. data_request_api/stable/query/data_request.py +1120 -0
  30. data_request_api/stable/query/dreq_classes.py +372 -0
  31. data_request_api/stable/query/dreq_query.py +981 -0
  32. data_request_api/stable/query/vocabulary_server.py +208 -0
  33. data_request_api/stable/utilities/__init__.py +0 -0
  34. data_request_api/stable/utilities/logger.py +71 -0
  35. data_request_api/stable/utilities/tools.py +49 -0
  36. data_request_api/version.py +16 -0
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Vocabulary server.
6
+ """
7
+
8
+ from __future__ import division, print_function, unicode_literals, absolute_import
9
+
10
+ import copy
11
+ from collections import defaultdict
12
+
13
+ from data_request_api.stable.utilities.logger import get_logger
14
+ from data_request_api.stable.utilities.tools import read_json_file
15
+
16
+
17
+ def is_link_id_or_value(elt):
18
+ """
19
+ Check if the input value is a link and transform it into a value if so
20
+ :param elt: element to be transformed into a value
21
+ :return: not link version oof elt
22
+ """
23
+ if isinstance(elt, str) and elt.startswith("link::"):
24
+ return True, elt.replace("link::", "")
25
+ else:
26
+ return False, elt
27
+
28
+
29
+ def build_link_from_id(elt):
30
+ """
31
+ Check if the input value is already a link and transform it if not
32
+ :param elt: element to be transformed into a link
33
+ :return: link version of elt
34
+ """
35
+ if not isinstance(elt, str) or elt.startswith("link::"):
36
+ return elt
37
+ else:
38
+ return f"link::{elt}"
39
+
40
+
41
+ class VocabularyServer(object):
42
+ """
43
+ Class to generate a Vocabulary Server from a json file.
44
+ """
45
+ def __init__(self, input_database, **kwargs):
46
+ self.vocabulary_server = copy.deepcopy(input_database)
47
+ self.version = self.vocabulary_server.pop("version")
48
+ self.check_infinite_loop()
49
+
50
+ @classmethod
51
+ def from_input(cls, input_database):
52
+ """
53
+ Generate VocabularyServer from a json file
54
+ :param input_database: json file name
55
+ :return:
56
+ """
57
+ content = read_json_file(input_database)
58
+ return cls(content)
59
+
60
+ def alias(self, element_type):
61
+ """
62
+ Find the real element_type if aliased
63
+ :param element_type: input kind of element
64
+ :return:
65
+ """
66
+ element_type_dict = dict(
67
+ keyword="glossary",
68
+ lead_theme="data_request_themes",
69
+ dimensions="coordinates_and_dimensions",
70
+ coordinates="coordinates_and_dimensions",
71
+ extra_dimensions="coordinates_and_dimensions"
72
+ )
73
+ return element_type_dict.get(element_type, element_type)
74
+
75
+ @staticmethod
76
+ def to_plural(element_type):
77
+ if not element_type.endswith("s"):
78
+ if element_type.endswith("y"):
79
+ element_type = element_type.rstrip("y") + "ies"
80
+ else:
81
+ element_type += "s"
82
+ return element_type
83
+
84
+ @staticmethod
85
+ def to_singular(element_type):
86
+ if element_type.endswith("ies"):
87
+ element_type = element_type.removesuffix("ies") + "y"
88
+ elif element_type.endswith("s"):
89
+ element_type = element_type.removesuffix("s")
90
+ return element_type
91
+
92
+ def check_infinite_loop(self):
93
+ """
94
+ Check that there is no infinite loop in the vocabulary server.
95
+ Raise an error if at least one is found.
96
+ """
97
+ logger = get_logger()
98
+ # Build the call dict
99
+ call_dict = defaultdict(set)
100
+ for key in self.vocabulary_server:
101
+ for id in self.vocabulary_server[key]:
102
+ for elt in self.vocabulary_server[key][id]:
103
+ if isinstance(self.vocabulary_server[key][id][elt], list) and \
104
+ any(is_link_id_or_value(subelt)[0] for subelt in self.vocabulary_server[key][id][elt]):
105
+ call_dict[key].add(elt)
106
+ elif not(isinstance(self.vocabulary_server[key][id][elt], list)) and \
107
+ is_link_id_or_value(self.vocabulary_server[key][id][elt])[0]:
108
+ call_dict[key].add(elt)
109
+
110
+ # Implement the function to be used
111
+ def follow_loop(current_key, former_keys=list()):
112
+ logger = get_logger()
113
+ found = False
114
+ alias_key, _ = self.get_element_type_ids(current_key)
115
+ if alias_key in former_keys:
116
+ logger.error(f"Infinite loop found: {former_keys + [current_key, ]}")
117
+ found = True
118
+ else:
119
+ for next_key in sorted(list(call_dict[alias_key])):
120
+ found = found or follow_loop(next_key, former_keys + [alias_key, ])
121
+ return found
122
+
123
+ # Follow the call dict to check if there is infinite loop
124
+ found = any(follow_loop(key) for key in sorted(list(call_dict)))
125
+ if found:
126
+ logger.critical("Infinite loop found in vocabulary server, see former error messages.")
127
+ raise ValueError("Infinite loop found in vocabulary server, see former error messages.")
128
+
129
+ def get_element_type_ids(self, element_type):
130
+ """
131
+ Get elements corresponding a a specific kind
132
+ :param element_type:
133
+ :return:
134
+ """
135
+ logger = get_logger()
136
+ element_type = self.alias(element_type)
137
+ if element_type not in self.vocabulary_server:
138
+ element_type = self.to_plural(element_type)
139
+ if element_type in self.vocabulary_server:
140
+ return element_type, sorted(list(self.vocabulary_server[element_type]))
141
+ else:
142
+ logger.error(f"Could not find element type {element_type} in the vocabulary server.")
143
+ raise ValueError(f"Could not find element type {element_type} in the vocabulary server.")
144
+
145
+ def get_element(self, element_type, element_id, element_key=None, default=False, id_type="id"):
146
+ """
147
+ Get an element corresponding to an element_id (corresponding to attribute id_type) of a kind element_type.
148
+ If element_key is specified, get the corresponding attribute.
149
+ If no element found, return default.
150
+ :param element_type:
151
+ :param element_id:
152
+ :param element_key:
153
+ :param default:
154
+ :param id_type:
155
+ :return:
156
+ """
157
+ logger = get_logger()
158
+ is_id, element_id = is_link_id_or_value(element_id)
159
+ if is_id or id_type != "id":
160
+ element_type, element_type_ids = self.get_element_type_ids(element_type)
161
+ found = False
162
+ if id_type in ["id", ] and element_id in element_type_ids:
163
+ value = self.vocabulary_server[element_type][element_id]
164
+ found = True
165
+ elif isinstance(id_type, str):
166
+ if element_id is None:
167
+ raise ValueError("None element_id found")
168
+ value = list()
169
+ for (key, val) in self.vocabulary_server[element_type].items():
170
+ val = val.get(id_type)
171
+ if (isinstance(val, list) and element_id in val) or element_id == val:
172
+ value.append(key)
173
+ if len(value) == 1:
174
+ found = True
175
+ element_id = value[0]
176
+ value = self.vocabulary_server[element_type][element_id]
177
+ elif len(value) > 1:
178
+ logger.error(f"id_type {id_type} provided is not unique for element type {element_type} and "
179
+ f"value {element_key}.")
180
+ raise ValueError(f"id_type {id_type} provided is not unique for element type {element_type} "
181
+ f"and value {element_key}.")
182
+ if found:
183
+ value = copy.deepcopy(value)
184
+ if element_key is not None:
185
+ if element_key in value:
186
+ value = value.get(element_key)
187
+ else:
188
+ logger.error(f"Could not find key {element_key} of id {element_id} of type {element_type} "
189
+ f"in the vocabulary server.")
190
+ raise ValueError(f"Could not find key {element_key} of id {element_id} of type "
191
+ f"{element_type} in the vocabulary server.")
192
+ elif isinstance(value, dict):
193
+ value["id"] = element_id
194
+ return value
195
+ elif default is not False:
196
+ logger.critical(f"Could not find {id_type} {element_id} of type {element_type}"
197
+ f" in the vocabulary server.")
198
+ return default
199
+ else:
200
+ logger.error(f"Could not find {id_type} {element_id} of type {element_type} "
201
+ f"in the vocabulary server.")
202
+ raise ValueError(f"Could not find {id_type} {element_id} of type {element_type} "
203
+ f"in the vocabulary server.")
204
+ elif element_id in ["???", None]:
205
+ logger.critical(f"Undefined id of type {element_type}")
206
+ return element_id
207
+ else:
208
+ return element_id
File without changes
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Logger.
5
+ """
6
+
7
+ from __future__ import unicode_literals, print_function, absolute_import, division
8
+
9
+ import logging
10
+ import os
11
+ import sys
12
+
13
+
14
+ log_dir = os.getcwd()
15
+ log_filename = "log.out"
16
+ log_file = os.sep.join([log_dir, log_filename])
17
+ log_level = "info"
18
+
19
+ logger = logging.getLogger()
20
+
21
+
22
+ def change_log_file(logfile=log_file, default=False):
23
+ global log_file, logger
24
+ if default:
25
+ logger = get_logger()
26
+ for hdlr in logger.handlers[:]:
27
+ hdlr.flush()
28
+ hdlr.close()
29
+ logger.removeHandler(hdlr)
30
+ new_hdlr = logging.StreamHandler(sys.stdout)
31
+ else:
32
+ log_file = logfile
33
+ logger = logging.getLogger()
34
+ for hdlr in logger.handlers[:]:
35
+ hdlr.flush()
36
+ hdlr.close()
37
+ logger.removeHandler(hdlr)
38
+ new_hdlr = logging.FileHandler(log_file)
39
+ new_hdlr.setFormatter(logging.Formatter(fmt='%(levelname)s: %(message)s'))
40
+ logger.addHandler(new_hdlr)
41
+ return logger
42
+
43
+
44
+ def get_logger():
45
+ return logger
46
+
47
+
48
+ def log_level_to_int(level):
49
+ if isinstance(level, str):
50
+ if level.lower() in ['debug', ]:
51
+ return logging.DEBUG
52
+ elif level.lower() in ['critical', ]:
53
+ return logging.CRITICAL
54
+ elif level.lower() in ['info', ]:
55
+ return logging.INFO
56
+ elif level.lower() in ['warning', ]:
57
+ return logging.WARNING
58
+ elif level.lower() in ['error', ]:
59
+ return logging.ERROR
60
+ else:
61
+ return level
62
+
63
+
64
+ def log_msg(level, *args, **kwargs):
65
+ logger.log(log_level_to_int(level), *args, **kwargs)
66
+
67
+
68
+ def change_log_level(level=log_level):
69
+ global logger, log_level
70
+ log_level = level
71
+ logger.setLevel(log_level_to_int(level))
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Tools for data request.
6
+ """
7
+ from __future__ import division, absolute_import, print_function, unicode_literals
8
+
9
+ import json
10
+ import os
11
+ import csv
12
+
13
+ from data_request_api.stable.utilities.logger import get_logger
14
+
15
+
16
+ def read_json_file(filename):
17
+ logger = get_logger()
18
+ if os.path.isfile(filename):
19
+ with open(filename, "r") as fic:
20
+ content = json.load(fic)
21
+ else:
22
+ logger.error(f"Filename {filename} is not readable")
23
+ raise OSError(f"Filename {filename} is not readable")
24
+ return content
25
+
26
+
27
+ def read_json_input_file_content(filename):
28
+ content = read_json_file(filename)
29
+ return content
30
+
31
+
32
+ def write_json_output_file_content(filename, content, **kwargs):
33
+ logger = get_logger()
34
+ logger.debug(f"Writing file {filename}.")
35
+ dirname = os.path.dirname(filename)
36
+ if not os.path.isdir(dirname):
37
+ logger.warning(f"Create directory {dirname}")
38
+ os.makedirs(dirname)
39
+ with open(filename, "w") as fic:
40
+ defaults = dict(indent=4, allow_nan=True, sort_keys=True)
41
+ defaults.update(kwargs)
42
+ json.dump(content, fic, **defaults)
43
+
44
+
45
+ def write_csv_output_file_content(filename, content, **kwargs):
46
+ with open(filename, 'w', newline='') as csvfile:
47
+ csvfile_content = csv.writer(csvfile, **kwargs)
48
+ for elt in content:
49
+ csvfile_content.writerow(elt)
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '1.1.2'
16
+ __version_tuple__ = version_tuple = (1, 1, 2)