spinta 0.2.dev18__py3-none-any.whl → 0.2.dev19__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.
spinta/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
1
  import importlib.metadata
2
2
 
3
3
  __version__ = importlib.metadata.version(__name__)
4
+
5
+ HTTP_URL_PREFIXES = ("http://", "https://")
@@ -6,7 +6,7 @@ from spinta.backends.helpers import get_table_name
6
6
  from spinta.backends.postgresql.helpers.name import get_pg_table_name, get_pg_column_name
7
7
  from spinta.core.ufuncs import Expr
8
8
  from spinta.types.datatype import Integer, Number, Boolean, String, Date, DateTime, Time, Ref
9
- from spinta import commands
9
+ from spinta import commands, HTTP_URL_PREFIXES
10
10
  from spinta.components import Context, Property
11
11
  from spinta.components import Model
12
12
  from spinta.exceptions import NotFoundError, NotImplementedFeature, InvalidRequestQuery
@@ -322,7 +322,7 @@ def summary(context: Context, dtype: Ref, backend: PostgreSQL, **kwargs):
322
322
  prefixes = dtype.model.external.dataset.prefixes
323
323
  label = None
324
324
  if uri and ":" in uri:
325
- if uri.startswith(("http://", "https://")):
325
+ if uri.startswith(HTTP_URL_PREFIXES):
326
326
  label = uri
327
327
  else:
328
328
  split = uri.split(":")
@@ -1,7 +1,8 @@
1
+ import dataclasses
1
2
  from enum import Enum
2
3
 
3
-
4
4
  from spinta.manifests.components import Manifest
5
+ from spinta.manifests.helpers import TypeDetector
5
6
 
6
7
 
7
8
  class DictFormat(Enum):
@@ -30,3 +31,103 @@ class XmlManifest(DictManifest):
30
31
  @staticmethod
31
32
  def detect_from_path(path: str) -> bool:
32
33
  return path.endswith(".xml")
34
+
35
+
36
+ @dataclasses.dataclass
37
+ class _MappedProperties:
38
+ name: str
39
+ source: str
40
+ extra: str
41
+ type_detector: TypeDetector
42
+
43
+
44
+ @dataclasses.dataclass
45
+ class _MappedModels:
46
+ name: str
47
+ source: str
48
+ properties: dict[str, _MappedProperties]
49
+
50
+
51
+ @dataclasses.dataclass
52
+ class _MappedDataset:
53
+ dataset: str
54
+ resource: str
55
+ models: dict[str, dict[str, _MappedModels]]
56
+
57
+
58
+ @dataclasses.dataclass
59
+ class _MappingMeta:
60
+ is_blank_node: bool
61
+ blank_node_name: str
62
+ blank_node_source: str
63
+ seperator: str
64
+ recursive_descent: str
65
+ remove_array_suffix: bool
66
+ model_source_prefix: str
67
+ check_namespace: bool
68
+ namespace_prefixes: dict[str, list[str]]
69
+ namespace_seperator: str
70
+
71
+ @classmethod
72
+ def get_for(cls, manifest_type: DictFormat) -> "_MappingMeta":
73
+ if manifest_type == DictFormat.JSON:
74
+ mapping_meta = _MappingMeta.for_json()
75
+ elif manifest_type in (DictFormat.XML, DictFormat.HTML):
76
+ mapping_meta = _MappingMeta.for_xml()
77
+ else:
78
+ mapping_meta = _MappingMeta.default()
79
+
80
+ return mapping_meta
81
+
82
+ @classmethod
83
+ def for_json(cls) -> "_MappingMeta":
84
+ return cls(
85
+ is_blank_node=False,
86
+ blank_node_name="model1",
87
+ blank_node_source=".",
88
+ seperator=".",
89
+ recursive_descent=".",
90
+ model_source_prefix="",
91
+ namespace_seperator=":",
92
+ remove_array_suffix=False,
93
+ check_namespace=False,
94
+ namespace_prefixes={},
95
+ )
96
+
97
+ @classmethod
98
+ def for_xml(cls) -> "_MappingMeta":
99
+ return cls(
100
+ is_blank_node=False,
101
+ blank_node_name="model1",
102
+ blank_node_source=".",
103
+ seperator="/",
104
+ recursive_descent="/..",
105
+ model_source_prefix="/",
106
+ namespace_seperator=":",
107
+ remove_array_suffix=True,
108
+ check_namespace=True,
109
+ namespace_prefixes={"xmlns": ["xmlns", "@xmlns"]},
110
+ )
111
+
112
+ @classmethod
113
+ def default(cls) -> "_MappingMeta":
114
+ return cls(
115
+ is_blank_node=False,
116
+ blank_node_name="model1",
117
+ blank_node_source=".",
118
+ seperator="",
119
+ recursive_descent="",
120
+ model_source_prefix="",
121
+ namespace_seperator=":",
122
+ remove_array_suffix=False,
123
+ check_namespace=False,
124
+ namespace_prefixes={},
125
+ )
126
+
127
+
128
+ @dataclasses.dataclass
129
+ class _MappingScope:
130
+ parent_scope: str
131
+ model_scope: str
132
+ model_name: str
133
+ property_name: str
@@ -4,70 +4,59 @@ from copy import deepcopy
4
4
 
5
5
  import requests
6
6
  import xmltodict
7
- from typing import List, Dict, Union, TypedDict, Any, Tuple, Callable
8
-
9
- from spinta.manifests.dict.components import DictFormat
7
+ from typing import Any, Iterator
8
+
9
+ from spinta import HTTP_URL_PREFIXES
10
+ from spinta.manifests.components import ManifestSchema
11
+ from spinta.manifests.dict.components import (
12
+ DictFormat,
13
+ _MappedDataset,
14
+ _MappingMeta,
15
+ _MappingScope,
16
+ _MappedModels,
17
+ _MappedProperties,
18
+ )
10
19
  from spinta.manifests.helpers import TypeDetector
11
20
  from spinta.utils.itertools import first_dict_value, first_dict_key
12
21
  from spinta.utils.naming import Deduplicator, to_model_name, to_property_name
13
22
 
14
23
 
15
- def read_schema(manifest_type: DictFormat, path: str, dataset_name: str):
16
- if path.startswith(("http://", "https://")):
24
+ def read_schema(manifest_type: DictFormat, path: str, dataset_name: str) -> Iterator[ManifestSchema]:
25
+ if path.startswith(HTTP_URL_PREFIXES):
17
26
  value = requests.get(path).text
18
27
  else:
19
28
  with pathlib.Path(path).open(encoding="utf-8-sig") as f:
20
29
  value = f.read()
21
30
 
22
31
  converted = {}
23
- mapping_meta = _MappingMeta(
24
- is_blank_node=False,
25
- blank_node_name="model1",
26
- blank_node_source=".",
27
- seperator="",
28
- recursive_descent="",
29
- model_source_prefix="",
30
- namespace_seperator=":",
31
- remove_array_suffix=False,
32
- check_namespace=False,
33
- namespace_prefixes={},
34
- detect_model=is_model,
35
- )
32
+ mapping_meta = _MappingMeta.get_for(manifest_type)
33
+
36
34
  if manifest_type == DictFormat.JSON:
37
35
  converted = json.loads(value)
38
- mapping_meta["seperator"] = "."
39
- mapping_meta["recursive_descent"] = "."
40
-
41
36
  elif manifest_type in (DictFormat.XML, DictFormat.HTML):
42
37
  converted = xmltodict.parse(value, cdata_key="text()")
43
- mapping_meta["seperator"] = "/"
44
- mapping_meta["recursive_descent"] = "/.."
45
- mapping_meta["model_source_prefix"] = "/"
46
- mapping_meta["remove_array_suffix"] = True
47
- mapping_meta["check_namespace"] = True
48
- mapping_meta["namespace_prefixes"] = {"xmlns": ["xmlns", "@xmlns"]}
49
38
 
50
39
  namespaces = list(extract_namespaces(converted, mapping_meta))
51
40
  converted = _fix_for_blank_nodes(converted)
52
41
  prefixes = {}
53
42
  for i, (key, value) in enumerate(namespaces):
54
43
  prefixes[key] = {"type": "prefix", "name": key, "uri": value, "eid": i}
55
- dataset_structure: _MappedDataset = {
56
- "dataset": dataset_name if dataset_name else "dataset",
57
- "resource": "resource",
58
- "models": {},
59
- }
60
- mapping_meta["is_blank_node"] = is_blank_node(converted)
44
+ dataset_structure = _MappedDataset(
45
+ dataset=dataset_name if dataset_name else "dataset",
46
+ resource="resource",
47
+ models={},
48
+ )
49
+ mapping_meta.is_blank_node = is_blank_node(converted)
61
50
  create_type_detectors(dataset_structure, converted, mapping_meta)
62
51
 
63
52
  yield (
64
53
  None,
65
54
  {
66
55
  "type": "dataset",
67
- "name": dataset_structure["dataset"],
56
+ "name": dataset_structure.dataset,
68
57
  "prefixes": prefixes,
69
58
  "resources": {
70
- dataset_structure["resource"]: {
59
+ dataset_structure.resource: {
71
60
  "type": f"dask/{manifest_type.value}",
72
61
  "external": path,
73
62
  },
@@ -78,38 +67,38 @@ def read_schema(manifest_type: DictFormat, path: str, dataset_name: str):
78
67
 
79
68
  dedup_model = Deduplicator("{}")
80
69
  blank_model = ""
81
- mapped_models: Dict[Tuple, str] = {}
70
+ mapped_models: dict[tuple, str] = {}
82
71
 
83
- for model in dataset_structure["models"].values():
72
+ for model in dataset_structure.models.values():
84
73
  for model_source, m in model.items():
85
- new_model = to_model_name(_name_without_namespace(m["name"], mapping_meta, prefixes))
74
+ new_model = to_model_name(_name_without_namespace(m.name, mapping_meta, prefixes))
86
75
  new_model = dedup_model(new_model)
87
- mapped_models[(m["name"], model_source)] = new_model
76
+ mapped_models[(m.name, model_source)] = new_model
88
77
 
89
- for model in dataset_structure["models"].values():
78
+ for model in dataset_structure.models.values():
90
79
  for model_source, m in model.items():
91
- if model_source == mapping_meta["blank_node_source"]:
92
- blank_model = mapped_models[(m["name"], model_source)]
80
+ if model_source == mapping_meta.blank_node_source:
81
+ blank_model = mapped_models[(m.name, model_source)]
93
82
  dedup_prop = Deduplicator("_{}")
94
83
  converted_props = {}
95
- for prop in m["properties"].values():
96
- new_prop = to_property_name(_name_without_namespace(prop["name"], mapping_meta, prefixes))
84
+ for prop in m.properties.values():
85
+ new_prop = to_property_name(_name_without_namespace(prop.name, mapping_meta, prefixes))
97
86
  new_prop = dedup_prop(new_prop)
98
87
  extra = {}
99
- type_detector = prop["type_detector"]
88
+ type_detector = prop.type_detector
100
89
  prop_type = type_detector.get_type()
101
90
  if prop_type == "ref":
102
- if prop["name"] in dataset_structure["models"].keys():
91
+ if prop.name in dataset_structure.models.keys():
103
92
  model_name = _name_without_namespace(
104
- mapped_models[prop["name"], prop["extra"]], mapping_meta, prefixes
93
+ mapped_models[prop.name, prop.extra], mapping_meta, prefixes
105
94
  )
106
- ref_model = f"{dataset_structure['dataset']}/{model_name}"
95
+ ref_model = f"{dataset_structure.dataset}/{model_name}"
107
96
  else:
108
- ref_model = f"{dataset_structure['dataset']}/{blank_model}"
97
+ ref_model = f"{dataset_structure.dataset}/{blank_model}"
109
98
  extra = {"model": ref_model}
110
99
  converted_props[new_prop] = {
111
100
  "type": prop_type,
112
- "external": {"name": prop["source"]},
101
+ "external": {"name": prop.source},
113
102
  "description": "",
114
103
  "required": type_detector.required,
115
104
  "unique": type_detector.unique,
@@ -122,19 +111,19 @@ def read_schema(manifest_type: DictFormat, path: str, dataset_name: str):
122
111
  copied["given_name"] = f"{new_prop}[]"
123
112
  converted_props[new_prop]["items"] = copied
124
113
 
125
- fixed_external_source = m["source"]
126
- if mapping_meta["remove_array_suffix"]:
114
+ fixed_external_source = m.source
115
+ if mapping_meta.remove_array_suffix:
127
116
  fixed_external_source = fixed_external_source.replace("[]", "")
128
117
  if not fixed_external_source.startswith("."):
129
- fixed_external_source = f"{mapping_meta['model_source_prefix']}{fixed_external_source}"
118
+ fixed_external_source = f"{mapping_meta.model_source_prefix}{fixed_external_source}"
130
119
  yield (
131
120
  None,
132
121
  {
133
122
  "type": "model",
134
- "name": f"{dataset_structure['dataset']}/{mapped_models[(m['name'], model_source)]}",
123
+ "name": f"{dataset_structure.dataset}/{mapped_models[(m.name, model_source)]}",
135
124
  "external": {
136
- "dataset": dataset_structure["dataset"],
137
- "resource": dataset_structure["resource"],
125
+ "dataset": dataset_structure.dataset,
126
+ "resource": dataset_structure.resource,
138
127
  "name": fixed_external_source,
139
128
  },
140
129
  "description": "",
@@ -147,80 +136,43 @@ def is_single_item_dict(value: dict) -> bool:
147
136
  return len(value) == 1
148
137
 
149
138
 
150
- def _fix_for_blank_nodes(values: Any):
151
- return_values = values
152
- if isinstance(values, dict) and is_single_item_dict(values):
153
- result = {}
154
- temp_dict = values
155
- while is_single_item_dict(temp_dict):
156
- first_key = first_dict_key(temp_dict)
157
- temp_dict = temp_dict[first_key]
158
-
159
- if not isinstance(temp_dict, dict):
160
- return return_values
161
-
162
- result[first_key] = temp_dict if is_single_item_dict(temp_dict) else [temp_dict]
139
+ def _fix_for_blank_nodes(values: Any) -> Any:
140
+ if not (isinstance(values, dict) and is_single_item_dict(values)):
141
+ return values
163
142
 
164
- return_values = result
165
- return return_values
166
-
167
-
168
- class _MappedProperties(TypedDict):
169
- name: str
170
- source: str
171
- extra: str
172
- type_detector: TypeDetector
173
-
174
-
175
- class _MappedModels(TypedDict):
176
- name: str
177
- source: str
178
- properties: Dict[str, _MappedProperties]
143
+ return_values = values
144
+ temp_dict = values
145
+ result = {}
179
146
 
147
+ while is_single_item_dict(temp_dict):
148
+ first_key = first_dict_key(temp_dict)
149
+ temp_dict = temp_dict[first_key]
180
150
 
181
- class _MappedDataset(TypedDict):
182
- dataset: str
183
- resource: str
184
- models: Dict[str, Dict[str, _MappedModels]]
151
+ if not isinstance(temp_dict, dict):
152
+ return return_values
185
153
 
154
+ result[first_key] = temp_dict if is_single_item_dict(temp_dict) else [temp_dict]
186
155
 
187
- class _MappingMeta(TypedDict):
188
- is_blank_node: bool
189
- blank_node_name: str
190
- blank_node_source: str
191
- seperator: str
192
- recursive_descent: str
193
- remove_array_suffix: bool
194
- model_source_prefix: str
195
- check_namespace: bool
196
- namespace_prefixes: dict
197
- namespace_seperator: str
198
- detect_model: Callable
156
+ return_values = result
157
+ return return_values
199
158
 
200
159
 
201
- class _MappingScope(TypedDict):
202
- parent_scope: str
203
- model_scope: str
204
- model_name: str
205
- property_name: str
160
+ def _name_without_namespace(name: str, mapping_meta: _MappingMeta, prefixes: dict) -> str:
161
+ if mapping_meta.namespace_seperator not in name:
162
+ return name
206
163
 
164
+ for prefix in prefixes.keys():
165
+ if (namespace := f"{prefix}{mapping_meta.namespace_seperator}") in name:
166
+ return name.replace(namespace, "")
207
167
 
208
- def _name_without_namespace(name: str, mapping_meta: _MappingMeta, prefixes: dict):
209
- if mapping_meta["namespace_seperator"] in name:
210
- for prefix in prefixes.keys():
211
- namespace = f"{prefix}{mapping_meta['namespace_seperator']}"
212
- if namespace in name:
213
- return name.replace(namespace, "")
214
168
  return name
215
169
 
216
170
 
217
- def is_model(data):
218
- if isinstance(data, list) and is_list_of_dicts(data):
219
- return True
220
- return False
171
+ def is_model(data: Any) -> bool:
172
+ return isinstance(data, list) and is_list_of_dicts(data)
221
173
 
222
174
 
223
- def _find_parent_key(data, string):
175
+ def _find_parent_key(data: dict[str, list[str]], string: str) -> str | None:
224
176
  for key, values in data.items():
225
177
  for value in values:
226
178
  if string.startswith(value):
@@ -228,29 +180,33 @@ def _find_parent_key(data, string):
228
180
  return None
229
181
 
230
182
 
231
- def extract_namespaces(data: Any, mapping_meta: _MappingMeta):
232
- if mapping_meta["check_namespace"]:
233
- if isinstance(data, dict):
234
- keys_to_remove = {}
235
- for key, value in data.items():
236
- parent = _find_parent_key(mapping_meta["namespace_prefixes"], key)
237
- if parent is not None:
238
- if key not in keys_to_remove.keys():
239
- return_key = key.split(mapping_meta["namespace_seperator"])
240
- if len(return_key) == 1:
241
- return_key = [parent]
242
- keys_to_remove[key] = (return_key[-1], value)
243
- for key, value in keys_to_remove.items():
244
- del data[key]
245
- yield value
246
- for value in data.values():
247
- yield from extract_namespaces(value, mapping_meta)
248
- elif isinstance(data, list):
249
- for item in data:
250
- yield from extract_namespaces(item, mapping_meta)
251
-
252
-
253
- def nested_prop_names(new_values: list, values: dict, root: str, seperator: str):
183
+ def extract_namespaces(data: Any, mapping_meta: _MappingMeta) -> Iterator[Any]:
184
+ if not mapping_meta.check_namespace:
185
+ return
186
+
187
+ if isinstance(data, dict):
188
+ keys_to_remove = {}
189
+ for key, value in data.items():
190
+ parent = _find_parent_key(mapping_meta.namespace_prefixes, key)
191
+ if parent is not None and key not in keys_to_remove.keys():
192
+ return_key = key.split(mapping_meta.namespace_seperator)
193
+ if len(return_key) == 1:
194
+ return_key = [parent]
195
+ keys_to_remove[key] = (return_key[-1], value)
196
+
197
+ for key, value in keys_to_remove.items():
198
+ del data[key]
199
+ yield value
200
+
201
+ for value in data.values():
202
+ yield from extract_namespaces(value, mapping_meta)
203
+
204
+ elif isinstance(data, list):
205
+ for item in data:
206
+ yield from extract_namespaces(item, mapping_meta)
207
+
208
+
209
+ def nested_prop_names(new_values: list, values: dict, root: str, seperator: str) -> None:
254
210
  for key, value in values.items():
255
211
  if isinstance(value, dict):
256
212
  nested_prop_names(new_values, value, f"{root}{seperator}{key}", seperator)
@@ -263,136 +219,150 @@ def nested_prop_names(new_values: list, values: dict, root: str, seperator: str)
263
219
 
264
220
  def check_missing_prop_required(
265
221
  dataset: _MappedDataset, values: dict, mapping_scope: _MappingScope, mapping_meta: _MappingMeta
266
- ):
267
- if mapping_scope["model_scope"] == "":
268
- model_name = (
269
- mapping_scope["model_name"] if mapping_scope["model_name"] != "" else first_dict_key(dataset["models"])
270
- )
271
- model_source = _create_name_with_prefix(mapping_scope["parent_scope"], mapping_meta["seperator"], model_name)
272
- if mapping_scope["model_name"] == "" and mapping_meta["is_blank_node"]:
273
- model_name = mapping_meta["blank_node_name"]
274
- model_source = mapping_meta["blank_node_source"]
275
- key_values = []
276
- for k, v in values.items():
277
- new_val = _create_name_with_prefix(mapping_scope["model_scope"], mapping_meta["seperator"], k)
278
- if isinstance(v, dict):
279
- nested_prop_names(key_values, v, new_val, mapping_meta["seperator"])
280
- elif isinstance(v, list):
281
- if not is_list_of_dicts(v):
282
- key_values.append(new_val)
283
- else:
284
- key_values.append(new_val)
285
- if key_values:
286
- filtered_models = dataset["models"][model_name][model_source]["properties"].keys()
287
- subtracted = set(filtered_models) - set(key_values)
288
- for prop in subtracted:
289
- type_detector = dataset["models"][model_name][model_source]["properties"][prop]["type_detector"]
290
- if type_detector.required and type_detector.type != "ref":
291
- type_detector.required = False
222
+ ) -> None:
223
+ if mapping_scope.model_scope != "":
224
+ return
225
+
226
+ model_name = mapping_scope.model_name if mapping_scope.model_name != "" else first_dict_key(dataset.models)
227
+ model_source = _create_name_with_prefix(mapping_scope.parent_scope, mapping_meta.seperator, model_name)
228
+ if mapping_scope.model_name == "" and mapping_meta.is_blank_node:
229
+ model_name = mapping_meta.blank_node_name
230
+ model_source = mapping_meta.blank_node_source
292
231
 
232
+ key_values = []
233
+ for key, value in values.items():
234
+ new_value = _create_name_with_prefix(mapping_scope.model_scope, mapping_meta.seperator, key)
235
+ if isinstance(value, dict):
236
+ nested_prop_names(key_values, value, new_value, mapping_meta.seperator)
237
+ elif isinstance(value, list):
238
+ if not is_list_of_dicts(value):
239
+ key_values.append(new_value)
240
+ else:
241
+ key_values.append(new_value)
242
+
243
+ if not key_values:
244
+ return
245
+
246
+ filtered_models = dataset.models[model_name][model_source].properties.keys()
247
+ subtracted = set(filtered_models) - set(key_values)
248
+ for prop in subtracted:
249
+ type_detector = dataset.models[model_name][model_source].properties[prop].type_detector
250
+ if type_detector.required and type_detector.type != "ref":
251
+ type_detector.required = False
293
252
 
294
- def run_type_detectors(dataset: _MappedDataset, values: dict, mapping_scope: _MappingScope, mapping_meta: _MappingMeta):
253
+
254
+ def run_type_detectors(
255
+ dataset: _MappedDataset, values: dict, mapping_scope: _MappingScope, mapping_meta: _MappingMeta
256
+ ) -> None:
295
257
  if isinstance(values, list):
296
258
  for item in values:
297
259
  run_type_detectors(dataset, item, mapping_scope, mapping_meta)
260
+
298
261
  elif isinstance(values, dict):
299
262
  check_missing_prop_required(dataset, values, mapping_scope, mapping_meta)
300
263
  for key, value in values.items():
301
- new_mapping_scope = _MappingScope(
302
- parent_scope=mapping_scope["parent_scope"],
303
- model_name=mapping_scope["model_name"],
304
- model_scope=mapping_scope["model_scope"],
305
- property_name=key,
306
- )
307
- if mapping_meta["detect_model"](value):
308
- new_mapping_scope["model_name"] = key
264
+ if is_model(value):
309
265
  parent_scope = _create_name_with_prefix(
310
- new_mapping_scope["parent_scope"],
311
- mapping_meta["seperator"],
312
- "" if mapping_scope["model_name"] == "" else f"{mapping_scope['model_name']}[]",
266
+ prefix=_create_name_with_prefix(
267
+ prefix=mapping_scope.parent_scope,
268
+ seperator=mapping_meta.seperator,
269
+ value="" if mapping_scope.model_name == "" else f"{mapping_scope.model_name}[]",
270
+ ),
271
+ seperator=mapping_meta.seperator,
272
+ value=mapping_scope.model_scope,
313
273
  )
314
- parent_scope = _create_name_with_prefix(
315
- parent_scope, mapping_meta["seperator"], new_mapping_scope["model_scope"]
274
+ new_mapping_scope = _MappingScope(
275
+ parent_scope=parent_scope,
276
+ model_name=key,
277
+ model_scope="",
278
+ property_name=key,
316
279
  )
317
- new_mapping_scope["model_scope"] = ""
318
- new_mapping_scope["parent_scope"] = parent_scope
319
280
  run_type_detectors(dataset, value, new_mapping_scope, mapping_meta)
320
281
  else:
282
+ new_mapping_scope = _MappingScope(
283
+ parent_scope=mapping_scope.parent_scope,
284
+ model_name=mapping_scope.model_name,
285
+ model_scope=mapping_scope.model_scope,
286
+ property_name=key,
287
+ )
321
288
  if isinstance(value, dict):
322
- new_mapping_scope["model_scope"] = _create_name_with_prefix(
323
- new_mapping_scope["model_scope"], mapping_meta["seperator"], key
289
+ new_mapping_scope.model_scope = _create_name_with_prefix(
290
+ prefix=new_mapping_scope.model_scope,
291
+ seperator=mapping_meta.seperator,
292
+ value=key,
324
293
  )
325
294
  run_type_detectors(dataset, value, new_mapping_scope, mapping_meta)
326
295
  else:
327
296
  _detect_type(dataset, new_mapping_scope, mapping_meta, value)
328
297
 
329
298
 
330
- def _detect_type(dataset: _MappedDataset, mapping_scope: _MappingScope, mapping_meta: _MappingMeta, value: Any):
331
- prop_name = _create_name_with_prefix(
332
- mapping_scope["model_scope"], mapping_meta["seperator"], mapping_scope["property_name"]
333
- )
334
- model_name = mapping_scope["model_name"]
299
+ def _detect_type(dataset: _MappedDataset, mapping_scope: _MappingScope, mapping_meta: _MappingMeta, value: Any) -> None:
300
+ prop_name = _create_name_with_prefix(mapping_scope.model_scope, mapping_meta.seperator, mapping_scope.property_name)
301
+ model_name = mapping_scope.model_name
335
302
  model_source = _create_name_with_prefix(
336
- mapping_scope["parent_scope"], mapping_meta["seperator"], mapping_scope["model_name"]
303
+ mapping_scope.parent_scope, mapping_meta.seperator, mapping_scope.model_name
337
304
  )
338
305
  if model_name == "":
339
- model_name = mapping_meta["blank_node_name"]
340
- model_source = mapping_meta["blank_node_source"]
341
- dataset["models"][model_name][model_source]["properties"][prop_name]["type_detector"].detect(value)
306
+ model_name = mapping_meta.blank_node_name
307
+ model_source = mapping_meta.blank_node_source
308
+ dataset.models[model_name][model_source].properties[prop_name].type_detector.detect(value)
342
309
 
343
310
 
344
- def is_list_of_dicts(lst: List) -> bool:
345
- for item in lst:
346
- if not isinstance(item, dict):
347
- return False
348
- return True
311
+ def is_list_of_dicts(data: list) -> bool:
312
+ return all(isinstance(item, dict) for item in data)
349
313
 
350
314
 
351
315
  def setup_model_type_detectors(
352
- dataset: _MappedDataset, values: Union[dict, list], mapping_scope: _MappingScope, mapping_meta: _MappingMeta
353
- ):
316
+ dataset: _MappedDataset, values: dict | list, mapping_scope: _MappingScope, mapping_meta: _MappingMeta
317
+ ) -> None:
354
318
  if isinstance(values, list):
355
319
  for item in values:
356
320
  setup_model_type_detectors(dataset, item, mapping_scope, mapping_meta)
321
+
357
322
  elif isinstance(values, dict):
358
323
  for key, value in values.items():
359
- new_mapping_scope = _MappingScope(
360
- parent_scope=mapping_scope["parent_scope"],
361
- model_name=mapping_scope["model_name"],
362
- model_scope=mapping_scope["model_scope"],
363
- property_name=key,
364
- )
365
- if mapping_meta["detect_model"](value):
366
- new_mapping_scope["model_name"] = key
324
+ if is_model(value):
367
325
  parent_scope = _create_name_with_prefix(
368
- new_mapping_scope["parent_scope"],
369
- mapping_meta["seperator"],
370
- "" if mapping_scope["model_name"] == "" else f"{mapping_scope['model_name']}[]",
326
+ prefix=_create_name_with_prefix(
327
+ prefix=mapping_scope.parent_scope,
328
+ seperator=mapping_meta.seperator,
329
+ value="" if mapping_scope.model_name == "" else f"{mapping_scope.model_name}[]",
330
+ ),
331
+ seperator=mapping_meta.seperator,
332
+ value=mapping_scope.model_scope,
371
333
  )
372
- parent_scope = _create_name_with_prefix(
373
- parent_scope, mapping_meta["seperator"], new_mapping_scope["model_scope"]
374
- )
375
- new_mapping_scope["model_scope"] = ""
376
- new_mapping_scope["parent_scope"] = parent_scope
377
334
 
335
+ new_mapping_scope = _MappingScope(
336
+ parent_scope=parent_scope,
337
+ model_name=key,
338
+ model_scope="",
339
+ property_name=key,
340
+ )
378
341
  setup_model_type_detectors(dataset, value, new_mapping_scope, mapping_meta)
379
342
 
380
- old_mapping_scope = new_mapping_scope.copy()
381
- old_mapping_scope["property_name"] = mapping_scope["property_name"]
343
+ old_mapping_scope = deepcopy(new_mapping_scope)
344
+ old_mapping_scope.property_name = mapping_scope.property_name
382
345
 
383
- if old_mapping_scope["property_name"] == "" and mapping_meta["is_blank_node"]:
384
- old_mapping_scope["property_name"] = "parent"
346
+ if old_mapping_scope.property_name == "" and mapping_meta.is_blank_node:
347
+ old_mapping_scope.property_name = "parent"
385
348
  set_type_detector(dataset, old_mapping_scope, mapping_meta, is_ref=True)
386
- elif old_mapping_scope["property_name"] != "":
349
+ elif old_mapping_scope.property_name != "":
387
350
  set_type_detector(dataset, old_mapping_scope, mapping_meta, is_ref=True)
351
+
388
352
  else:
353
+ new_mapping_scope = _MappingScope(
354
+ parent_scope=mapping_scope.parent_scope,
355
+ model_name=mapping_scope.model_name,
356
+ model_scope=mapping_scope.model_scope,
357
+ property_name=key,
358
+ )
389
359
  if isinstance(value, list):
390
360
  set_type_detector(dataset, new_mapping_scope, mapping_meta, is_array=True)
391
361
  elif isinstance(value, dict):
392
- new_mapping_scope["model_scope"] = _create_name_with_prefix(
393
- new_mapping_scope["model_scope"], mapping_meta["seperator"], key
362
+ new_mapping_scope.model_scope = _create_name_with_prefix(
363
+ new_mapping_scope.model_scope, mapping_meta.seperator, key
394
364
  )
395
- new_mapping_scope["property_name"] = mapping_scope["property_name"]
365
+ new_mapping_scope.property_name = mapping_scope.property_name
396
366
  setup_model_type_detectors(dataset, value, new_mapping_scope, mapping_meta)
397
367
  else:
398
368
  set_type_detector(dataset, new_mapping_scope, mapping_meta)
@@ -401,18 +371,22 @@ def setup_model_type_detectors(
401
371
  def _create_name_with_prefix(prefix: str, seperator: str, value: str) -> str:
402
372
  if value == "":
403
373
  return prefix
404
- return value if prefix == "" else f"{prefix}{seperator}{value}"
374
+ if prefix == "":
375
+ return value
376
+
377
+ return f"{prefix}{seperator}{value}"
405
378
 
406
379
 
407
- def create_type_detectors(dataset: _MappedDataset, values: Any, mapping_meta: _MappingMeta):
380
+ def create_type_detectors(dataset: _MappedDataset, values: Any, mapping_meta: _MappingMeta) -> None:
408
381
  mapping_scope = _MappingScope(parent_scope="", model_scope="", model_name="", property_name="")
409
382
  setup_model_type_detectors(dataset, values, mapping_scope, mapping_meta)
410
383
  run_type_detectors(dataset, values, mapping_scope, mapping_meta)
411
384
 
412
385
 
413
- def is_blank_node(values: Union[list, dict]) -> bool:
386
+ def is_blank_node(values: list | dict) -> bool:
414
387
  if isinstance(values, list):
415
388
  return True
389
+
416
390
  if isinstance(values, dict):
417
391
  temp_dict = values
418
392
  first_value = first_dict_value(temp_dict)
@@ -429,6 +403,7 @@ def is_blank_node(values: Union[list, dict]) -> bool:
429
403
  for key, value in values.items():
430
404
  if not isinstance(value, list):
431
405
  return True
406
+
432
407
  return False
433
408
 
434
409
 
@@ -438,75 +413,74 @@ def set_type_detector(
438
413
  mapping_meta: _MappingMeta,
439
414
  is_ref: bool = False,
440
415
  is_array: bool = False,
441
- ):
442
- if mapping_scope["property_name"] != "":
443
- model_name = mapping_scope["model_name"]
444
- model_source = _create_name_with_prefix(mapping_scope["parent_scope"], mapping_meta["seperator"], model_name)
445
- prop_name = _create_name_with_prefix(
446
- mapping_scope["model_scope"], mapping_meta["seperator"], mapping_scope["property_name"]
447
- )
448
- prop_source = prop_name
449
- if model_name == "" and mapping_meta["is_blank_node"]:
450
- model_name = mapping_meta["blank_node_name"]
451
- model_source = mapping_meta["blank_node_source"]
452
- extra = ""
453
- if is_ref:
454
- extra = ""
455
- split = mapping_scope["parent_scope"].split(mapping_meta["seperator"])
456
- count = 0
457
- for i in reversed(split):
458
- if i.endswith("[]"):
459
- half = mapping_scope["parent_scope"].split(i)
460
- extra = i[:-2] if half[0] == "" else f"{half[0]}{i[:-2]}"
461
- break
462
- elif i != "":
463
- count += 1
464
- prop_source = f"..{mapping_meta['recursive_descent'] * count}"
465
-
466
- if model_name in dataset["models"].keys():
467
- if model_source in dataset["models"][model_name].keys():
468
- if prop_name not in dataset["models"][model_name][model_source]["properties"].keys():
469
- dataset["models"][model_name][model_source]["properties"][prop_name] = {
470
- "name": prop_name,
471
- "source": prop_source,
472
- "type_detector": TypeDetector(),
473
- "extra": extra,
474
- }
475
- else:
476
- dataset["models"][model_name][model_source] = {
477
- "name": model_name,
478
- "source": model_source,
479
- "properties": {
480
- prop_name: {
481
- "name": prop_name,
482
- "source": prop_source,
483
- "type_detector": TypeDetector(),
484
- "extra": extra,
485
- }
486
- },
487
- }
416
+ ) -> None:
417
+ if mapping_scope.property_name == "":
418
+ return
419
+
420
+ model_name = mapping_scope.model_name
421
+ model_source = _create_name_with_prefix(mapping_scope.parent_scope, mapping_meta.seperator, model_name)
422
+ prop_name = _create_name_with_prefix(mapping_scope.model_scope, mapping_meta.seperator, mapping_scope.property_name)
423
+ prop_source = prop_name
424
+ if model_name == "" and mapping_meta.is_blank_node:
425
+ model_name = mapping_meta.blank_node_name
426
+ model_source = mapping_meta.blank_node_source
427
+ extra = ""
428
+ if is_ref:
429
+ split = mapping_scope.parent_scope.split(mapping_meta.seperator)
430
+ count = 0
431
+ for i in reversed(split):
432
+ if i.endswith("[]"):
433
+ half = mapping_scope.parent_scope.split(i)
434
+ extra = i[:-2] if half[0] == "" else f"{half[0]}{i[:-2]}"
435
+ break
436
+ elif i != "":
437
+ count += 1
438
+ prop_source = f"..{mapping_meta.recursive_descent * count}"
439
+
440
+ if model_name in dataset.models.keys():
441
+ if model_source in dataset.models[model_name].keys():
442
+ if prop_name not in dataset.models[model_name][model_source].properties.keys():
443
+ dataset.models[model_name][model_source].properties[prop_name] = _MappedProperties(
444
+ name=prop_name,
445
+ source=prop_source,
446
+ type_detector=TypeDetector(),
447
+ extra=extra,
448
+ )
488
449
  else:
489
- dataset["models"][model_name] = {
490
- model_source: {
491
- "name": model_name,
492
- "source": model_source,
493
- "properties": {
494
- prop_name: {
495
- "name": prop_name,
496
- "source": prop_source,
497
- "type_detector": TypeDetector(),
498
- "extra": extra,
499
- }
500
- },
501
- }
502
- }
503
-
504
- type_detector = dataset["models"][model_name][model_source]["properties"][prop_name]["type_detector"]
505
- if is_array:
506
- type_detector.array = True
507
- type_detector.unique = False
508
- type_detector.required = False
509
- if is_ref:
510
- type_detector.type = "ref"
511
- type_detector.unique = False
512
- type_detector.required = False
450
+ dataset.models[model_name][model_source] = _MappedModels(
451
+ name=model_name,
452
+ source=model_source,
453
+ properties={
454
+ prop_name: _MappedProperties(
455
+ name=prop_name,
456
+ source=prop_source,
457
+ type_detector=TypeDetector(),
458
+ extra=extra,
459
+ )
460
+ },
461
+ )
462
+ else:
463
+ dataset.models[model_name] = {
464
+ model_source: _MappedModels(
465
+ name=model_name,
466
+ source=model_source,
467
+ properties={
468
+ prop_name: _MappedProperties(
469
+ name=prop_name,
470
+ source=prop_source,
471
+ type_detector=TypeDetector(),
472
+ extra=extra,
473
+ )
474
+ },
475
+ )
476
+ }
477
+
478
+ type_detector = dataset.models[model_name][model_source].properties[prop_name].type_detector
479
+ if is_array:
480
+ type_detector.array = True
481
+ type_detector.unique = False
482
+ type_detector.required = False
483
+ if is_ref:
484
+ type_detector.type = "ref"
485
+ type_detector.unique = False
486
+ type_detector.required = False
@@ -8,7 +8,7 @@ from typing import Type
8
8
 
9
9
  import jsonpatch
10
10
 
11
- from spinta import commands
11
+ from spinta import commands, HTTP_URL_PREFIXES
12
12
  from spinta.backends.constants import BackendOrigin
13
13
  from spinta.backends.helpers import load_backend
14
14
  from spinta.components import Model
@@ -324,7 +324,7 @@ def get_manifest_from_type(rc: RawConfig, type_: str) -> Type[Manifest]:
324
324
 
325
325
 
326
326
  def check_manifest_path(manifest: Manifest, path: str) -> None:
327
- if not path.startswith(("http://", "https://")) and not pathlib.Path(path).exists():
327
+ if not path.startswith(HTTP_URL_PREFIXES) and not pathlib.Path(path).exists():
328
328
  raise ManifestFileDoesNotExist(manifest, path=path)
329
329
 
330
330
 
@@ -432,7 +432,7 @@ class TypeDetector:
432
432
  self.type = new_type
433
433
 
434
434
  def _assert_url(self, value: str):
435
- if value.startswith(("http://", "https://")):
435
+ if value.startswith(HTTP_URL_PREFIXES):
436
436
  self.type = "url"
437
437
  else:
438
438
  self.type = ""
@@ -1,4 +1,5 @@
1
1
  import dataclasses
2
+ import logging
2
3
  from operator import itemgetter
3
4
  from typing import Any, TypedDict
4
5
  from typing import Dict
@@ -28,6 +29,9 @@ from spinta.utils.naming import to_model_name
28
29
  from spinta.utils.naming import to_property_name
29
30
 
30
31
 
32
+ logger = logging.getLogger(__name__)
33
+
34
+
31
35
  def read_schema(context: Context, path: str, prepare: str = None, dataset_name: str = ""):
32
36
  engine = sa.create_engine(path)
33
37
  schema = None
@@ -236,6 +240,7 @@ def _read_props(
236
240
  )
237
241
 
238
242
 
243
+ UNKNOWN_TYPE = "UNKNOWN"
239
244
  TYPES = [
240
245
  (sa.Boolean, "boolean"),
241
246
  (sa.Date, "date"),
@@ -273,12 +278,13 @@ class _Column(TypedDict):
273
278
  type: TypeEngine
274
279
 
275
280
 
276
- def _get_column_type(column: _Column, table: str = None) -> str:
281
+ def _get_column_type(column: _Column, table: str | None = None) -> str:
277
282
  column_type: TypeEngine = column["type"]
278
283
  for cls, name in TYPES:
279
284
  if isinstance(column_type, cls):
280
285
  return name
281
- raise TypeError(f"Unknown type {column_type!r} of column {column!r} in table {table!r}.")
286
+ logger.warning(f"Unknown type {column_type!r} of column {column!r} in table {table!r}. Column will be skipped.")
287
+ return UNKNOWN_TYPE
282
288
 
283
289
 
284
290
  class _Ref(NamedTuple):
spinta/nodes.py CHANGED
@@ -14,6 +14,7 @@ from spinta.components import Context
14
14
  from spinta.components import EntryId
15
15
  from spinta.components import Node
16
16
  from spinta.manifests.components import Manifest
17
+ from spinta.manifests.sql.helpers import UNKNOWN_TYPE
17
18
  from spinta.manifests.tabular.constants import DataTypeEnum
18
19
  from spinta.utils.schema import NA
19
20
  from spinta.utils.schema import resolve_schema
@@ -221,6 +222,9 @@ def load_model_properties(
221
222
  model.leafprops = {}
222
223
  model.properties = {}
223
224
  for name, params in data.items():
225
+ # Do not load properties with unmapped types.
226
+ if params["type"] == UNKNOWN_TYPE:
227
+ continue
224
228
  prop = Prop()
225
229
  prop.name = name
226
230
  prop.place = name
spinta/testing/pytest.py CHANGED
@@ -97,8 +97,12 @@ def mongo(rc):
97
97
  if dsn and db:
98
98
  import pymongo
99
99
 
100
- client = pymongo.MongoClient(dsn)
101
- client.drop_database(db)
100
+ try:
101
+ client = pymongo.MongoClient(dsn, serverSelectionTimeoutMS=2000)
102
+ client.server_info() # force connection check
103
+ client.drop_database(db)
104
+ except pymongo.errors.ServerSelectionTimeoutError:
105
+ pass # MongoDB not running, nothing to clean up
102
106
 
103
107
 
104
108
  @pytest.fixture(scope="session")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spinta
3
- Version: 0.2.dev18
3
+ Version: 0.2.dev19
4
4
  Summary: A platform for describing, extracting, transforming, loading and serving open data.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- spinta/__init__.py,sha256=IILIKzO2OO60Rhsh3SqhdKHDf7XI_RLRwzztBGZZmHY,78
1
+ spinta/__init__.py,sha256=OLVp_IMRnqoX_Bxzh2exv1p4N0NSTPTBVVjhX74qFCU,123
2
2
  spinta/__main__.py,sha256=3n1t_DsFmpQZyAtqPrfXyHAPjpBoS-Oj9hPPFSi2IPw,70
3
3
  spinta/accesslog/__init__.py,sha256=svgEqf0U1ykvxQ61pRuAzadwdn7TnQriQ4a9A_aMoKI,5938
4
4
  spinta/accesslog/file.py,sha256=h1KcGsbIPoI2ZzXXQ2b8eZFOP0EvkTudfWDmwy25zlg,2150
@@ -107,7 +107,7 @@ spinta/backends/postgresql/commands/migrate/types/string.py,sha256=C2jo9AH8YTLlc
107
107
  spinta/backends/postgresql/commands/migrate/types/text.py,sha256=E8Pa4GpCIq2JLNRxkkF5YIs4hfpqy4AVQgGXAfplzoI,5767
108
108
  spinta/backends/postgresql/commands/read.py,sha256=h_kMFdb3-wl5i-3yh6b4dr-09GVNgUos65NDWMf0Igk,2908
109
109
  spinta/backends/postgresql/commands/redirect.py,sha256=nc1cHobA3JsmXn3h5otKNIU-a1us3ouTNKjhr6N7zMk,1810
110
- spinta/backends/postgresql/commands/summary.py,sha256=opYAn1cD_VIsgcM-YGcT1xJj6AaQCBuovQm5VPEHh24,17572
110
+ spinta/backends/postgresql/commands/summary.py,sha256=bwbLVMB1MVAHpGrRRLy38PpNedUt-se3AHx4pdhQjX4,17585
111
111
  spinta/backends/postgresql/commands/validate.py,sha256=Q6NEK3vIkd1k4jLg9pGRKKw6PfelS52m-BORGg4nO_Y,615
112
112
  spinta/backends/postgresql/commands/wait.py,sha256=Yr_NF3hprX-KhKAqRcddrnRJZazsCWud3yYYI1fi2yg,647
113
113
  spinta/backends/postgresql/commands/wipe.py,sha256=o0mZuYVr5xbQfO3lqxpBcVtiZE0FqOJyAm738XGf36Q,2279
@@ -522,9 +522,9 @@ spinta/manifests/dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
522
522
  spinta/manifests/dict/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
523
523
  spinta/manifests/dict/commands/configure.py,sha256=P74uhCZKdr6nc70gBBkr1tvsrv5JjKowMx7QQMwzJVA,537
524
524
  spinta/manifests/dict/commands/load.py,sha256=eILuiDz5mSrB93fdxseYl8gZNUtUXwVnQJQ06CatrTg,1417
525
- spinta/manifests/dict/components.py,sha256=9G3tBzyf2JdvJVQoukQqSI-0Q-Idf7YLauhYzS4XItc,646
526
- spinta/manifests/dict/helpers.py,sha256=v8Zwa-87INdHIF50Bw5XmddzojAOLUhr5BE-34gH0_E,20994
527
- spinta/manifests/helpers.py,sha256=qCwauueoCzIwck1s4njqBXQrogk4bonot9WOX-foifE,14611
525
+ spinta/manifests/dict/components.py,sha256=fY7IKoArTrPhKaMukwXYP3IfYdJETHRzhUaNrAt4xWs,3281
526
+ spinta/manifests/dict/helpers.py,sha256=AzLQMMdvwvcAb7XWgmJtvDgVEyazN0GgPS9A6DRX3oo,19235
527
+ spinta/manifests/helpers.py,sha256=k_QBpWeWeMTIA9oSN-y5QjUcwJV8-CEL1fVJCGqAKGI,14618
528
528
  spinta/manifests/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
529
529
  spinta/manifests/internal/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
530
530
  spinta/manifests/internal/commands/configure.py,sha256=pNSEwita8LCnn1r7Xs6BiijEd9LW8kqa24oIjeDAP-Y,462
@@ -567,7 +567,7 @@ spinta/manifests/sql/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
567
567
  spinta/manifests/sql/commands/configure.py,sha256=j_ypYAV9o0pTS_qazw1oBCuRohSXfGPs_tmrDUIEVNk,539
568
568
  spinta/manifests/sql/commands/load.py,sha256=xHdoo7dWHZ9RtCUo7J0R89DyFFFUKG7XV-0vVgE9SHo,1806
569
569
  spinta/manifests/sql/components.py,sha256=lG44u_ufsqDm3kjP8_zshYg87J5S_UVzYWJ5Ea74G1w,661
570
- spinta/manifests/sql/helpers.py,sha256=doJ7XAXCUFrhc0JWbhRGLFeDVVDz-wLRWgtYw5GQZ5s,14720
570
+ spinta/manifests/sql/helpers.py,sha256=LgnWWU4P2UMVC9zp0Dpwk_5ZnLCXQnlPWv4Jp8aApk8,14853
571
571
  spinta/manifests/tabular/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
572
572
  spinta/manifests/tabular/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
573
573
  spinta/manifests/tabular/commands/bootstrap.py,sha256=hdINMkcwBKXlpKPPXXJqdMjWpLb5WEigf-RfEFcEUkE,438
@@ -610,7 +610,7 @@ spinta/naming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
610
610
  spinta/naming/components.py,sha256=TnuJg3fB9ftLOV6C2QI2lI-e3_OhlFdpI-qWW0wDeS4,210
611
611
  spinta/naming/helpers.py,sha256=tmey39J56_4YQ-jvsVInon4NJkyzM6i91hN_RMtt9kM,4599
612
612
  spinta/naming/ufuncts.py,sha256=gRGgyt_jjqn0-QHLc7BWGtJRb4qdaBCc8I90mdzJeYc,1334
613
- spinta/nodes.py,sha256=1SU_vvZB0P67-waZFs46BYuD9AB5nKgQie1mmUS_bjc,7898
613
+ spinta/nodes.py,sha256=UQJU0rQK2avVUkvQ6oXz3GQkZIvVAd0zaXJQpp5XTlg,8070
614
614
  spinta/renderer.py,sha256=D0hlDOFFdjfjZ4x6WV9pK5Lozs0fbpLYd-IcOmtNoR0,761
615
615
  spinta/spyna.py,sha256=lwBjhpxKoZtnEMmIvvGlCy0yLKfqmU575F-Axr2K5t4,11528
616
616
  spinta/templates/base.html,sha256=tRMf48yngJ-zPDP9mP-Q9TkQW4VXIXkdqpodzwFGF0w,2092
@@ -629,7 +629,7 @@ spinta/testing/dtypes.py,sha256=5qGBuCjDJywTJSvJmvA62DvY8H-dJFti441wtGkrvO0,6288
629
629
  spinta/testing/manifest.py,sha256=HCrgCu9QMry_iNtuGkQI7-hp6m5xmtSMUTzwpW0Z6y8,6121
630
630
  spinta/testing/migration.py,sha256=q-fQqjTtTZKdJKagYFYAspkGQHhenYoO4O2x-Amamdw,7979
631
631
  spinta/testing/push.py,sha256=t_2PpWQI-95_rNUhvBZTHtOQhaIpTO0nXHd5nigI6mM,531
632
- spinta/testing/pytest.py,sha256=NwCXiX36V2YxZ_1Hbg38ewj77fKEaobX1Gcf5d26C98,10261
632
+ spinta/testing/pytest.py,sha256=ouXo_G8N3IEGAQZ74YlJhAzW7KPbv-20_6a0bc1OeXY,10492
633
633
  spinta/testing/request.py,sha256=vJ5uJASCE21w9WFwvYaB5zDHfL3d744LNwLi6kk8fXQ,2605
634
634
  spinta/testing/tabular.py,sha256=_AEi6Lf_rcIlWO-55XfAYx-1sdHSBtyFMlsXGg1C8DE,1114
635
635
  spinta/testing/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -748,8 +748,8 @@ spinta/utils/tree.py,sha256=iF8eOSBagUoDmdJGNQgsYB_gshsFajmiUALXqU9luHE,591
748
748
  spinta/utils/types.py,sha256=lfYSxKGPuPeUsO14d2OYodtbRY3zsa-o-z8HveVH3t0,801
749
749
  spinta/utils/units.py,sha256=CFFLv1NHYsoSSzwiar3zXYmt4m3sccW5niUgkZQgo3k,747
750
750
  spinta/utils/url.py,sha256=b6sqQEpmCdT3oV4vGDzXnN8w415InAYjIW_o2djhQS8,2950
751
- spinta-0.2.dev18.dist-info/METADATA,sha256=j6sJ8LCHnWqtnSLRfX9YEDVBmms2np9CdPF9rglQvO0,10207
752
- spinta-0.2.dev18.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
753
- spinta-0.2.dev18.dist-info/entry_points.txt,sha256=-jdsOQZcMu3rUOwgIJNS3gZS4rwWPACuXXy128F676w,46
754
- spinta-0.2.dev18.dist-info/licenses/LICENSE,sha256=JKmjfBLapeFWNI_qdVr5bXGlsuMPa6nRarKPK5davKw,1071
755
- spinta-0.2.dev18.dist-info/RECORD,,
751
+ spinta-0.2.dev19.dist-info/METADATA,sha256=uWFAtzdrYe0mMg-4oVZH6LKnB9hB8aG-cdhgK62AqIY,10207
752
+ spinta-0.2.dev19.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
753
+ spinta-0.2.dev19.dist-info/entry_points.txt,sha256=-jdsOQZcMu3rUOwgIJNS3gZS4rwWPACuXXy128F676w,46
754
+ spinta-0.2.dev19.dist-info/licenses/LICENSE,sha256=JKmjfBLapeFWNI_qdVr5bXGlsuMPa6nRarKPK5davKw,1071
755
+ spinta-0.2.dev19.dist-info/RECORD,,