jsonstat-validator 0.1.0__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.
- jsonstat_validator/__init__.py +34 -0
- jsonstat_validator/tests/__init__.py +1 -0
- jsonstat_validator/tests/conftest.py +10 -0
- jsonstat_validator/tests/custom_tests.py +411 -0
- jsonstat_validator/tests/samples/areas/canada.json +16 -0
- jsonstat_validator/tests/samples/areas/galicia.json +16 -0
- jsonstat_validator/tests/samples/areas/index.json +31 -0
- jsonstat_validator/tests/samples/areas/oecd.json +16 -0
- jsonstat_validator/tests/samples/areas/us.json +26 -0
- jsonstat_validator/tests/samples/canada.json +131 -0
- jsonstat_validator/tests/samples/datasets/index.json +51 -0
- jsonstat_validator/tests/samples/galicia.json +124 -0
- jsonstat_validator/tests/samples/hierarchy.json +200 -0
- jsonstat_validator/tests/samples/index.json +36 -0
- jsonstat_validator/tests/samples/metrics/gdp/gsp.json +16 -0
- jsonstat_validator/tests/samples/metrics/gdp/gsppc.json +16 -0
- jsonstat_validator/tests/samples/metrics/gdp/gspw.json +16 -0
- jsonstat_validator/tests/samples/metrics/gdp/index.json +26 -0
- jsonstat_validator/tests/samples/metrics/index.json +26 -0
- jsonstat_validator/tests/samples/metrics/lfs/employed.json +16 -0
- jsonstat_validator/tests/samples/metrics/lfs/index.json +31 -0
- jsonstat_validator/tests/samples/metrics/lfs/lf.json +16 -0
- jsonstat_validator/tests/samples/metrics/lfs/unemployed.json +16 -0
- jsonstat_validator/tests/samples/metrics/lfs/unr.json +21 -0
- jsonstat_validator/tests/samples/metrics/pop/index.json +21 -0
- jsonstat_validator/tests/samples/metrics/pop/pop.json +26 -0
- jsonstat_validator/tests/samples/metrics/pop/popw.json +16 -0
- jsonstat_validator/tests/samples/oecd.json +170 -0
- jsonstat_validator/tests/samples/order.json +38 -0
- jsonstat_validator/tests/samples/sources/bls.json +21 -0
- jsonstat_validator/tests/samples/sources/ige.json +16 -0
- jsonstat_validator/tests/samples/sources/index.json +41 -0
- jsonstat_validator/tests/samples/sources/jsonstat.json +21 -0
- jsonstat_validator/tests/samples/sources/oecd.json +16 -0
- jsonstat_validator/tests/samples/sources/statcan.json +16 -0
- jsonstat_validator/tests/samples/sources/wikipedia.json +16 -0
- jsonstat_validator/tests/samples/topics/accounts.json +16 -0
- jsonstat_validator/tests/samples/topics/demos.json +21 -0
- jsonstat_validator/tests/samples/topics/index.json +31 -0
- jsonstat_validator/tests/samples/topics/labor.json +26 -0
- jsonstat_validator/tests/samples/topics/population.json +26 -0
- jsonstat_validator/tests/samples/us-gsp.json +230 -0
- jsonstat_validator/tests/samples/us-labor.json +6509 -0
- jsonstat_validator/tests/samples/us-unr.json +3269 -0
- jsonstat_validator/tests/test_official_samples.py +51 -0
- jsonstat_validator/validator.py +608 -0
- jsonstat_validator-0.1.0.dist-info/LICENSE +21 -0
- jsonstat_validator-0.1.0.dist-info/METADATA +196 -0
- jsonstat_validator-0.1.0.dist-info/RECORD +51 -0
- jsonstat_validator-0.1.0.dist-info/WHEEL +5 -0
- jsonstat_validator-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
"""
|
2
|
+
JSON-stat validator.
|
3
|
+
|
4
|
+
A validator for the JSON-stat 2.0 format, a simple lightweight format for data
|
5
|
+
interchange. It provides a way to exchange data associated to dimensions,
|
6
|
+
following the cube model that is so common in statistical offices.
|
7
|
+
|
8
|
+
For more information on JSON-stat, see: https://json-stat.org/
|
9
|
+
"""
|
10
|
+
|
11
|
+
from jsonstat_validator.validator import (
|
12
|
+
Category,
|
13
|
+
Collection,
|
14
|
+
Dataset,
|
15
|
+
DatasetRole,
|
16
|
+
Dimension,
|
17
|
+
JSONStatSchema,
|
18
|
+
Link,
|
19
|
+
Unit,
|
20
|
+
validate_jsonstat,
|
21
|
+
)
|
22
|
+
|
23
|
+
__version__ = "0.1.0"
|
24
|
+
__all__ = [
|
25
|
+
"Dataset",
|
26
|
+
"Dimension",
|
27
|
+
"Collection",
|
28
|
+
"Link",
|
29
|
+
"Unit",
|
30
|
+
"Category",
|
31
|
+
"DatasetRole",
|
32
|
+
"JSONStatSchema",
|
33
|
+
"validate_jsonstat",
|
34
|
+
]
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Test package for JSON-stat validator."""
|
@@ -0,0 +1,411 @@
|
|
1
|
+
"""Custom test cases for the JSON-stat validator tool."""
|
2
|
+
|
3
|
+
import copy
|
4
|
+
from datetime import datetime
|
5
|
+
|
6
|
+
import pytest
|
7
|
+
|
8
|
+
from jsonstat_validator import Collection, Dataset, Dimension, validate_jsonstat
|
9
|
+
|
10
|
+
|
11
|
+
# --- Helper functions ---
|
12
|
+
def current_iso8601():
|
13
|
+
"""Return the current ISO 8601 datetime as a string."""
|
14
|
+
return datetime.now().isoformat()
|
15
|
+
|
16
|
+
|
17
|
+
# --- Valid base structures ---
|
18
|
+
MINIMAL_DATASET = {
|
19
|
+
"version": "2.0",
|
20
|
+
"class": "dataset",
|
21
|
+
"id": ["time", "geo"],
|
22
|
+
"size": [2, 3],
|
23
|
+
"value": [1, 2, 3, 4, 5, 6],
|
24
|
+
"dimension": {
|
25
|
+
"time": {"category": {"index": ["2020", "2021"]}},
|
26
|
+
"geo": {"category": {"index": {"US": 0, "EU": 1, "AS": 2}}},
|
27
|
+
},
|
28
|
+
}
|
29
|
+
|
30
|
+
MINIMAL_DIMENSION = {
|
31
|
+
"version": "2.0",
|
32
|
+
"class": "dimension",
|
33
|
+
"category": {"index": ["male", "female"]},
|
34
|
+
}
|
35
|
+
|
36
|
+
MINIMAL_COLLECTION = {
|
37
|
+
"version": "2.0",
|
38
|
+
"class": "collection",
|
39
|
+
"link": {
|
40
|
+
"item": [
|
41
|
+
{
|
42
|
+
"class": "dataset",
|
43
|
+
"href": "https://json-stat.org/samples/oecd.json",
|
44
|
+
"label": "Unemployment rate in the OECD countries 2003-2014",
|
45
|
+
}
|
46
|
+
]
|
47
|
+
},
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
class TestValidCases:
|
52
|
+
"""Test cases for valid JSON-stat objects."""
|
53
|
+
|
54
|
+
def test_minimal_dataset(self):
|
55
|
+
"""Test that a minimal dataset validates successfully."""
|
56
|
+
assert validate_jsonstat(MINIMAL_DATASET) is True
|
57
|
+
|
58
|
+
def test_sparse_dataset_values(self):
|
59
|
+
"""Test that a dataset with sparse values validates successfully."""
|
60
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
61
|
+
data["value"] = {
|
62
|
+
"0:0": 1,
|
63
|
+
"0:1": 2,
|
64
|
+
"0:2": 3,
|
65
|
+
"1:0": 4,
|
66
|
+
"1:1": 5,
|
67
|
+
"1:2": 6,
|
68
|
+
}
|
69
|
+
assert validate_jsonstat(data) is True
|
70
|
+
|
71
|
+
def test_dataset_with_roles(self):
|
72
|
+
"""Test that a dataset with roles validates successfully."""
|
73
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
74
|
+
data["role"] = {
|
75
|
+
"time": ["time"],
|
76
|
+
"geo": ["geo"],
|
77
|
+
}
|
78
|
+
assert validate_jsonstat(data) is True
|
79
|
+
|
80
|
+
def test_dimension_with_label(self):
|
81
|
+
"""Test that a dimension with a label validates successfully."""
|
82
|
+
data = copy.deepcopy(MINIMAL_DIMENSION)
|
83
|
+
data["label"] = "Gender"
|
84
|
+
data["category"]["label"] = {"male": "Male", "female": "Female"}
|
85
|
+
assert validate_jsonstat(data) is True
|
86
|
+
|
87
|
+
def test_collection_with_nested_items(self):
|
88
|
+
"""Test that a collection with nested items validates successfully."""
|
89
|
+
data = copy.deepcopy(MINIMAL_COLLECTION)
|
90
|
+
data["link"]["item"].append(
|
91
|
+
{
|
92
|
+
"class": "collection",
|
93
|
+
"href": "https://json-stat.org/samples/collection.json",
|
94
|
+
"label": "A nested collection",
|
95
|
+
}
|
96
|
+
)
|
97
|
+
assert validate_jsonstat(data) is True
|
98
|
+
|
99
|
+
|
100
|
+
class TestInvalidCases:
|
101
|
+
"""Test cases for invalid JSON-stat objects."""
|
102
|
+
|
103
|
+
def test_missing_required_field(self):
|
104
|
+
"""Test that a dataset missing a required field fails validation."""
|
105
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
106
|
+
del data["id"]
|
107
|
+
with pytest.raises(ValueError):
|
108
|
+
validate_jsonstat(data)
|
109
|
+
|
110
|
+
def test_size_length_mismatch(self):
|
111
|
+
"""Test that a dataset with mismatched size and id length fails validation."""
|
112
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
113
|
+
data["size"] = [2, 3, 4] # One more than id length
|
114
|
+
with pytest.raises(ValueError):
|
115
|
+
validate_jsonstat(data)
|
116
|
+
|
117
|
+
def test_invalid_status_format(self):
|
118
|
+
"""Test that a dataset with an invalid status format fails validation."""
|
119
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
120
|
+
data["status"] = ["a", "b", "c"] # Too few elements
|
121
|
+
with pytest.raises(ValueError):
|
122
|
+
validate_jsonstat(data)
|
123
|
+
|
124
|
+
def test_missing_dimension_definition(self):
|
125
|
+
"""Test that a dataset with a missing dimension definition fails validation."""
|
126
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
127
|
+
data["id"] = ["time", "geo", "metric"] # Added a new dimension
|
128
|
+
data["size"] = [2, 3, 2] # Updated size accordingly
|
129
|
+
# But the dimension is not defined
|
130
|
+
with pytest.raises(ValueError):
|
131
|
+
validate_jsonstat(data)
|
132
|
+
|
133
|
+
def test_invalid_collection_link_key(self):
|
134
|
+
"""Test that a collection with an invalid link key fails validation."""
|
135
|
+
data = copy.deepcopy(MINIMAL_COLLECTION)
|
136
|
+
# Collections must use 'item' as the relation type
|
137
|
+
data["link"] = {"invalid_key": data["link"]["item"]}
|
138
|
+
with pytest.raises(ValueError):
|
139
|
+
validate_jsonstat(data)
|
140
|
+
|
141
|
+
def test_unit_missing_decimals(self):
|
142
|
+
"""Test that a unit missing the required decimals field fails validation."""
|
143
|
+
dimension = copy.deepcopy(MINIMAL_DIMENSION)
|
144
|
+
dimension["category"]["unit"] = {"male": {"label": "Count"}}
|
145
|
+
with pytest.raises(ValueError):
|
146
|
+
validate_jsonstat(dimension)
|
147
|
+
|
148
|
+
def test_category_missing_index_and_label(self):
|
149
|
+
"""Test that a category missing both index and label fails validation."""
|
150
|
+
dimension = copy.deepcopy(MINIMAL_DIMENSION)
|
151
|
+
# Remove index and don't provide label
|
152
|
+
del dimension["category"]["index"]
|
153
|
+
with pytest.raises(ValueError):
|
154
|
+
validate_jsonstat(dimension)
|
155
|
+
|
156
|
+
|
157
|
+
class TestTypeValidation:
|
158
|
+
"""Test cases for type validation in JSON-stat objects."""
|
159
|
+
|
160
|
+
@pytest.mark.parametrize(
|
161
|
+
"field,value",
|
162
|
+
[
|
163
|
+
("version", 2.0), # Should be a string
|
164
|
+
("class", "invalid_class"), # Invalid class value
|
165
|
+
("size", ["not_number"]), # Should be integers
|
166
|
+
("id", "not_list"), # Should be a list
|
167
|
+
("value", "not_array_or_dict"), # Should be array or dict
|
168
|
+
],
|
169
|
+
)
|
170
|
+
def test_invalid_types(self, field, value):
|
171
|
+
"""Test that invalid types fail validation."""
|
172
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
173
|
+
data[field] = value
|
174
|
+
with pytest.raises(ValueError):
|
175
|
+
validate_jsonstat(data)
|
176
|
+
|
177
|
+
def test_link_validation(self):
|
178
|
+
"""Test that link validation works correctly."""
|
179
|
+
collection = copy.deepcopy(MINIMAL_COLLECTION)
|
180
|
+
# Invalid URL format
|
181
|
+
collection["link"]["item"][0]["href"] = "invalid-url"
|
182
|
+
with pytest.raises(ValueError):
|
183
|
+
validate_jsonstat(collection)
|
184
|
+
|
185
|
+
|
186
|
+
class TestClassValues:
|
187
|
+
"""Test cases for class values in JSON-stat objects."""
|
188
|
+
|
189
|
+
@pytest.mark.parametrize(
|
190
|
+
"model_class,input_data,expected",
|
191
|
+
[
|
192
|
+
(Dataset, MINIMAL_DATASET, "dataset"),
|
193
|
+
(Dimension, MINIMAL_DIMENSION, "dimension"),
|
194
|
+
(Collection, MINIMAL_COLLECTION, "collection"),
|
195
|
+
],
|
196
|
+
)
|
197
|
+
def test_class_values(self, model_class, input_data, expected):
|
198
|
+
"""Test that class values are validated correctly."""
|
199
|
+
model = model_class.model_validate(input_data)
|
200
|
+
assert model.class_ == expected
|
201
|
+
|
202
|
+
def test_invalid_class_value(self):
|
203
|
+
"""Test that an invalid class value fails validation."""
|
204
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
205
|
+
data["class"] = "invalid_class"
|
206
|
+
with pytest.raises(ValueError):
|
207
|
+
validate_jsonstat(data)
|
208
|
+
|
209
|
+
|
210
|
+
class TestComplexStructures:
|
211
|
+
"""Test cases for complex JSON-stat structures."""
|
212
|
+
|
213
|
+
def test_nested_collections(self):
|
214
|
+
"""Test that nested collections validate successfully."""
|
215
|
+
# A collection containing a dataset and another collection
|
216
|
+
collection = {
|
217
|
+
"version": "2.0",
|
218
|
+
"class": "collection",
|
219
|
+
"link": {
|
220
|
+
"item": [
|
221
|
+
{
|
222
|
+
"class": "dataset",
|
223
|
+
"href": "https://json-stat.org/samples/oecd.json",
|
224
|
+
},
|
225
|
+
{
|
226
|
+
"class": "collection",
|
227
|
+
"link": {
|
228
|
+
"item": [
|
229
|
+
{
|
230
|
+
"class": "dataset",
|
231
|
+
"href": "https://json-stat.org/samples/canada.json",
|
232
|
+
}
|
233
|
+
]
|
234
|
+
},
|
235
|
+
},
|
236
|
+
]
|
237
|
+
},
|
238
|
+
}
|
239
|
+
assert validate_jsonstat(collection) is True
|
240
|
+
|
241
|
+
def test_multi_level_category_hierarchy(self):
|
242
|
+
"""Test that a multi-level category hierarchy validates successfully."""
|
243
|
+
dimension = copy.deepcopy(MINIMAL_DIMENSION)
|
244
|
+
dimension["category"]["index"] = ["total", "a", "a1", "a2", "b", "b1", "b2"]
|
245
|
+
dimension["category"]["child"] = {
|
246
|
+
"total": ["a", "b"],
|
247
|
+
"a": ["a1", "a2"],
|
248
|
+
"b": ["b1", "b2"],
|
249
|
+
}
|
250
|
+
assert validate_jsonstat(dimension) is True
|
251
|
+
|
252
|
+
def test_mixed_value_status_formats(self):
|
253
|
+
"""Test that a dataset with mixed value and status formats validates."""
|
254
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
255
|
+
data["value"] = {
|
256
|
+
"0:0": 1,
|
257
|
+
"0:1": 2,
|
258
|
+
"0:2": 3,
|
259
|
+
"1:0": 4,
|
260
|
+
"1:1": 5,
|
261
|
+
"1:2": 6,
|
262
|
+
}
|
263
|
+
data["status"] = {"0:0": "a", "1:1": "b"}
|
264
|
+
assert validate_jsonstat(data) is True
|
265
|
+
|
266
|
+
|
267
|
+
class TestEdgeCases:
|
268
|
+
"""Test cases for edge cases in JSON-stat objects."""
|
269
|
+
|
270
|
+
def test_empty_dataset_values(self):
|
271
|
+
"""Test that a dataset with empty values validates successfully."""
|
272
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
273
|
+
data["value"] = []
|
274
|
+
data["size"] = []
|
275
|
+
data["id"] = []
|
276
|
+
data["dimension"] = {}
|
277
|
+
assert validate_jsonstat(data) is True
|
278
|
+
|
279
|
+
def test_single_category_dimension(self):
|
280
|
+
"""Test that a dimension with a single category validates without index."""
|
281
|
+
dimension = {
|
282
|
+
"version": "2.0",
|
283
|
+
"class": "dimension",
|
284
|
+
"category": {"label": {"a": "A"}},
|
285
|
+
}
|
286
|
+
assert validate_jsonstat(dimension) is True
|
287
|
+
|
288
|
+
def test_iso8601_datetime_format(self):
|
289
|
+
"""Test that ISO 8601 datetime formats are validated correctly."""
|
290
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
291
|
+
# Valid formats
|
292
|
+
data["updated"] = "2022-01-01T12:30:45Z"
|
293
|
+
assert validate_jsonstat(data) is True
|
294
|
+
data["updated"] = "2022-01-01T12:30:45+01:00"
|
295
|
+
assert validate_jsonstat(data) is True
|
296
|
+
# Invalid format
|
297
|
+
data["updated"] = "01/01/2022"
|
298
|
+
with pytest.raises(ValueError):
|
299
|
+
validate_jsonstat(data)
|
300
|
+
|
301
|
+
def test_extension_fields(self):
|
302
|
+
"""Test that extension fields are allowed in JSON-stat objects."""
|
303
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
304
|
+
data["extension"] = {"custom_field": "custom_value"}
|
305
|
+
assert validate_jsonstat(data) is True
|
306
|
+
|
307
|
+
def test_mixed_value_types(self):
|
308
|
+
"""Test that datasets with mixed value types validate successfully."""
|
309
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
310
|
+
data["value"] = [1, "2", 3.0, None, 5, 6]
|
311
|
+
assert validate_jsonstat(data) is True
|
312
|
+
|
313
|
+
def test_unit_position_validation(self):
|
314
|
+
"""Test that unit position is validated correctly."""
|
315
|
+
dimension = {
|
316
|
+
"version": "2.0",
|
317
|
+
"class": "dimension",
|
318
|
+
"category": {
|
319
|
+
"index": ["gdp", "pop"],
|
320
|
+
"unit": {
|
321
|
+
"gdp": {
|
322
|
+
"decimals": 1,
|
323
|
+
"symbol": "$",
|
324
|
+
"position": "start",
|
325
|
+
},
|
326
|
+
"pop": {
|
327
|
+
"decimals": 0,
|
328
|
+
"symbol": "people",
|
329
|
+
"position": "end",
|
330
|
+
},
|
331
|
+
},
|
332
|
+
},
|
333
|
+
}
|
334
|
+
assert validate_jsonstat(dimension) is True
|
335
|
+
|
336
|
+
# Invalid position value
|
337
|
+
dimension["category"]["unit"]["gdp"]["position"] = "invalid"
|
338
|
+
with pytest.raises(ValueError):
|
339
|
+
validate_jsonstat(dimension)
|
340
|
+
|
341
|
+
def test_coordinates_validation(self):
|
342
|
+
"""Test that coordinates are validated correctly."""
|
343
|
+
dimension = {
|
344
|
+
"version": "2.0",
|
345
|
+
"class": "dimension",
|
346
|
+
"category": {
|
347
|
+
"index": ["US", "CA", "MX"],
|
348
|
+
"coordinates": {
|
349
|
+
"US": [-98.5795, 39.8282],
|
350
|
+
"CA": [-106.3468, 56.1304],
|
351
|
+
"MX": [-102.5528, 23.6345],
|
352
|
+
},
|
353
|
+
},
|
354
|
+
}
|
355
|
+
assert validate_jsonstat(dimension) is True
|
356
|
+
# Invalid coordinates format
|
357
|
+
dimension["category"]["coordinates"]["US"] = [-98.5795] # Missing latitude
|
358
|
+
with pytest.raises(ValueError):
|
359
|
+
validate_jsonstat(dimension)
|
360
|
+
|
361
|
+
def test_child_parent_validation(self):
|
362
|
+
"""Test that child references to parent categories are validated."""
|
363
|
+
dimension = {
|
364
|
+
"version": "2.0",
|
365
|
+
"class": "dimension",
|
366
|
+
"category": {
|
367
|
+
"index": ["total", "a", "b"],
|
368
|
+
"child": {"total": ["a", "b"]},
|
369
|
+
},
|
370
|
+
}
|
371
|
+
assert validate_jsonstat(dimension) is True
|
372
|
+
# Invalid parent reference
|
373
|
+
dimension["category"]["child"] = {"invalid_parent": ["a", "b"]}
|
374
|
+
with pytest.raises(ValueError):
|
375
|
+
validate_jsonstat(dimension)
|
376
|
+
|
377
|
+
def test_unit_category_validation(self):
|
378
|
+
"""Test that unit references to categories are validated."""
|
379
|
+
dimension = {
|
380
|
+
"version": "2.0",
|
381
|
+
"class": "dimension",
|
382
|
+
"category": {
|
383
|
+
"index": ["gdp", "pop"],
|
384
|
+
"unit": {
|
385
|
+
"gdp": {"decimals": 1},
|
386
|
+
"pop": {"decimals": 0},
|
387
|
+
},
|
388
|
+
},
|
389
|
+
}
|
390
|
+
assert validate_jsonstat(dimension) is True
|
391
|
+
# Invalid category reference
|
392
|
+
dimension["category"]["unit"] = {"invalid_category": {"decimals": 1}}
|
393
|
+
with pytest.raises(ValueError):
|
394
|
+
validate_jsonstat(dimension)
|
395
|
+
|
396
|
+
def test_multi_role_validation(self):
|
397
|
+
"""Test that dimensions referenced in multiple roles are validated."""
|
398
|
+
data = copy.deepcopy(MINIMAL_DATASET)
|
399
|
+
# Valid: different dimensions in different roles
|
400
|
+
data["role"] = {
|
401
|
+
"time": ["time"],
|
402
|
+
"geo": ["geo"],
|
403
|
+
}
|
404
|
+
assert validate_jsonstat(data) is True
|
405
|
+
# Invalid: same dimension in multiple roles
|
406
|
+
data["role"] = {
|
407
|
+
"time": ["time", "geo"],
|
408
|
+
"geo": ["geo"],
|
409
|
+
}
|
410
|
+
with pytest.raises(ValueError):
|
411
|
+
validate_jsonstat(data)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"version" : "2.0",
|
3
|
+
"class" : "collection",
|
4
|
+
"href" : "http://json-stat.org/samples/areas/canada.json",
|
5
|
+
"label" : "Area: Canada",
|
6
|
+
"updated" : "2016-01-01",
|
7
|
+
"link" : {
|
8
|
+
"item" : [
|
9
|
+
{
|
10
|
+
"class" : "dataset",
|
11
|
+
"href" : "http://json-stat.org/samples/canada.json",
|
12
|
+
"label" : "Population by sex and age group. Canada. 2012"
|
13
|
+
}
|
14
|
+
]
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"version" : "2.0",
|
3
|
+
"class" : "collection",
|
4
|
+
"href" : "http://json-stat.org/samples/areas/galicia.json",
|
5
|
+
"label" : "Area: Galicia",
|
6
|
+
"updated" : "2016-01-01",
|
7
|
+
"link" : {
|
8
|
+
"item" : [
|
9
|
+
{
|
10
|
+
"class" : "dataset",
|
11
|
+
"href" : "http://json-stat.org/samples/galicia.json",
|
12
|
+
"label" : "Population by province of residence, place of birth, age, gender and year in Galicia"
|
13
|
+
}
|
14
|
+
]
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{
|
2
|
+
"version" : "2.0",
|
3
|
+
"class" : "collection",
|
4
|
+
"href" : "http://json-stat.org/samples/areas/index.json",
|
5
|
+
"label" : "Areas",
|
6
|
+
"updated" : "2016-01-01",
|
7
|
+
"link" : {
|
8
|
+
"item" : [
|
9
|
+
{
|
10
|
+
"class" : "collection",
|
11
|
+
"href" : "http://json-stat.org/samples/areas/us.json",
|
12
|
+
"label" : "Area: United States"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"class" : "collection",
|
16
|
+
"href" : "http://json-stat.org/samples/areas/canada.json",
|
17
|
+
"label" : "Area: Canada"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"class" : "collection",
|
21
|
+
"href" : "http://json-stat.org/samples/areas/galicia.json",
|
22
|
+
"label" : "Area: Galicia"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
"class" : "collection",
|
26
|
+
"href" : "http://json-stat.org/samples/areas/oecd.json",
|
27
|
+
"label" : "Area: OECD Zone"
|
28
|
+
}
|
29
|
+
]
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"version" : "2.0",
|
3
|
+
"class" : "collection",
|
4
|
+
"href" : "http://json-stat.org/samples/areas/oecd.json",
|
5
|
+
"label" : "Area: OECD Zone",
|
6
|
+
"updated" : "2016-01-01",
|
7
|
+
"link" : {
|
8
|
+
"item" : [
|
9
|
+
{
|
10
|
+
"class" : "dataset",
|
11
|
+
"href" : "http://json-stat.org/samples/oecd.json",
|
12
|
+
"label" : "Unemployment rate in the OECD countries 2003-2014"
|
13
|
+
}
|
14
|
+
]
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"version" : "2.0",
|
3
|
+
"class" : "collection",
|
4
|
+
"href" : "http://json-stat.org/samples/areas/us.json",
|
5
|
+
"label" : "Area: United States",
|
6
|
+
"updated" : "2016-01-01",
|
7
|
+
"link" : {
|
8
|
+
"item" : [
|
9
|
+
{
|
10
|
+
"class" : "dataset",
|
11
|
+
"href" : "http://json-stat.org/samples/us-gsp.json",
|
12
|
+
"label" : "US States by GSP and population"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"class" : "dataset",
|
16
|
+
"href" : "http://json-stat.org/samples/us-unr.json",
|
17
|
+
"label" : "Unemployment Rates by County, 2012 Annual Averages"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"class" : "dataset",
|
21
|
+
"href" : "http://json-stat.org/samples/us-labor.json",
|
22
|
+
"label" : "Labor Force Data by County, 2012 Annual Averages"
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
}
|