python-re3data-v2 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.
- python_re3data_v2-0.1.0.dist-info/METADATA +215 -0
- python_re3data_v2-0.1.0.dist-info/RECORD +19 -0
- python_re3data_v2-0.1.0.dist-info/WHEEL +4 -0
- python_re3data_v2-0.1.0.dist-info/entry_points.txt +2 -0
- python_re3data_v2-0.1.0.dist-info/licenses/LICENSE +21 -0
- re3data/__init__.py +31 -0
- re3data/__main__.py +6 -0
- re3data/_cli.py +114 -0
- re3data/_client/__init__.py +14 -0
- re3data/_client/_async.py +143 -0
- re3data/_client/_sync.py +145 -0
- re3data/_client/base.py +152 -0
- re3data/_exceptions.py +9 -0
- re3data/_parser.py +443 -0
- re3data/_resources/__init__.py +19 -0
- re3data/_resources/repository.py +564 -0
- re3data/_resources/repository_list.py +66 -0
- re3data/_response.py +34 -0
- re3data/_serializer.py +82 -0
re3data/_serializer.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Serialize parsed re3data resources into dict, JSON and CSV representations.
|
|
2
|
+
|
|
3
|
+
Dict conventions (matching the contract established by the original
|
|
4
|
+
python-re3data library):
|
|
5
|
+
|
|
6
|
+
- Keys are the XML element/attribute names (camelCase).
|
|
7
|
+
- Elements with text and attributes become ``{"value": ..., "language": ...}``.
|
|
8
|
+
- Dates are rendered as ISO-8601 strings (``2023-12-06``).
|
|
9
|
+
- Absent optional scalars are ``None``; absent repeats are empty lists.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import csv
|
|
15
|
+
import io
|
|
16
|
+
import json
|
|
17
|
+
from dataclasses import asdict, is_dataclass
|
|
18
|
+
from datetime import date
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
from re3data._resources import RepositorySummary
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from re3data._resources import Repository
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _normalize(node: Any) -> Any:
|
|
28
|
+
"""Recursively convert non-JSON-native leaves into JSON-native ones."""
|
|
29
|
+
if isinstance(node, date):
|
|
30
|
+
return node.isoformat()
|
|
31
|
+
if is_dataclass(node):
|
|
32
|
+
return {key: _normalize(value) for key, value in asdict(node).items()}
|
|
33
|
+
if isinstance(node, dict):
|
|
34
|
+
return {key: _normalize(value) for key, value in node.items()}
|
|
35
|
+
if isinstance(node, (list, tuple)):
|
|
36
|
+
return [_normalize(item) for item in node]
|
|
37
|
+
return node
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _to_dict(parsed: Repository | list[RepositorySummary]) -> dict[str, Any] | list[dict[str, Any]]:
|
|
41
|
+
"""Serialize a parsed resource into a dictionary."""
|
|
42
|
+
return _normalize(parsed)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _to_json(parsed: Repository | list[RepositorySummary]) -> str:
|
|
46
|
+
"""Serialize a parsed resource into a JSON string."""
|
|
47
|
+
return json.dumps(_normalize(parsed), indent=2, ensure_ascii=False)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _repository_summary_row(summary: RepositorySummary) -> dict[str, Any]:
|
|
51
|
+
return {
|
|
52
|
+
"id": summary.id,
|
|
53
|
+
"doi": summary.doi,
|
|
54
|
+
"name": summary.name,
|
|
55
|
+
"href": summary.href,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _to_csv(parsed: Repository | list[RepositorySummary]) -> str:
|
|
60
|
+
"""Serialize repository summaries into a CSV string.
|
|
61
|
+
|
|
62
|
+
Only list responses produce tabular output; a single repository detail is
|
|
63
|
+
rendered as one summary row.
|
|
64
|
+
"""
|
|
65
|
+
summaries: list[RepositorySummary]
|
|
66
|
+
if isinstance(parsed, list):
|
|
67
|
+
summaries = parsed
|
|
68
|
+
else:
|
|
69
|
+
summaries = [
|
|
70
|
+
RepositorySummary(
|
|
71
|
+
id=parsed.id,
|
|
72
|
+
doi=parsed.identifiers.doi if parsed.identifiers else None,
|
|
73
|
+
name=parsed.repositoryName.value if parsed.repositoryName else None,
|
|
74
|
+
link=None,
|
|
75
|
+
)
|
|
76
|
+
]
|
|
77
|
+
buffer = io.StringIO()
|
|
78
|
+
writer = csv.DictWriter(buffer, fieldnames=["id", "doi", "name", "href"], lineterminator="\n")
|
|
79
|
+
writer.writeheader()
|
|
80
|
+
for summary in summaries:
|
|
81
|
+
writer.writerow(_repository_summary_row(summary))
|
|
82
|
+
return buffer.getvalue().rstrip("\n")
|