forked from datacommonsorg/api-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpayloads.py
More file actions
139 lines (109 loc) · 4.91 KB
/
payloads.py
File metadata and controls
139 lines (109 loc) · 4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from typing import Optional
from pydantic import Field
from pydantic import field_serializer
from pydantic import field_validator
from pydantic import model_serializer
from pydantic import model_validator
from datacommons_client.models.base import BaseDCModel
from datacommons_client.models.base import ListOrStr
from datacommons_client.models.observation import ObservationDate
from datacommons_client.models.observation import ObservationSelect
from datacommons_client.models.observation import ObservationSelectList
def normalize_list_to_string(value: str | list[str]) -> str:
"""Converts a list of properties to a string."""
if isinstance(value, list):
return f"[{', '.join(value)}]"
return value
class NodeRequestPayload(BaseDCModel):
"""
A Pydantic model to structure, normalize, and validate the payload for a Node V2 API request.
Attributes:
node_dcids (str | list[str]): The DCID(s) of the nodes to query.
expression (str): The property or relation expression(s) to query.
"""
node_dcids: ListOrStr = Field(..., serialization_alias="nodes")
expression: list | str = Field(..., serialization_alias="property")
class ObservationRequestPayload(BaseDCModel):
"""
A Pydantic model to structure, normalize, and validate the payload for an Observation V2 API request.
Attributes:
date (str): The date for which data is being requested.
variable_dcids (str | list[str]): One or more variable IDs for the data.
select (list[ObservationSelect]): Fields to include in the response.
Defaults to ["date", "variable", "entity", "value"].
entity_dcids (Optional[str | list[str]]): One or more entity IDs to filter the data.
entity_expression (Optional[str]): A string expression to filter entities.
filter_facet_domains (Optional[str | list[str]]): One or more domain names to filter the data.
filter_facet_ids (Optional[str | list[str]]): One or more facet IDs to filter the data.
"""
date: ObservationDate | str = Field(default_factory=str,
validate_default=True)
variable_dcids: Optional[ListOrStr] = Field(default=None,
serialization_alias="variable")
select: Optional[list[str]] = None
entity_dcids: Optional[ListOrStr] = None
entity_expression: Optional[str | list[str]] = None
filter_facet_domains: Optional[ListOrStr] = None
filter_facet_ids: Optional[ListOrStr] = None
@field_validator("date", mode="before")
def _validate_date(cls, v):
try:
return ObservationDate(v)
except ValueError:
return v
@field_validator("select", mode="before")
def _coerce_select(cls, v):
return ObservationSelectList.model_validate(v).select
@field_validator("entity_expression", mode="before")
def _coerce_expr(cls, v):
if v is None:
return v
if isinstance(v, list):
return normalize_list_to_string(v)
if isinstance(v, str):
return v
raise TypeError("expression must be a string or list[str]")
@field_serializer("variable_dcids", "entity_dcids", when_used="unless-none")
def _serialise_dcids_fields(self, v):
return {"dcids": v}
@field_serializer("entity_expression", when_used="unless-none")
def _serialise_expression_field(self, v):
return {"expression": v}
@model_validator(mode="after")
def _check_one(self):
if bool(self.entity_dcids) == bool(self.entity_expression):
raise ValueError("Exactly one of dcids or expression must be set")
return self
@model_serializer(mode="wrap")
def _wrap_filter(self, handler):
# Normal dump
data = handler(self)
# pull out entity dcid or expression
entity = data.pop("entity_dcids", None) or data.pop("entity_expression",
None)
# add entity to the data dictionary
data["entity"] = entity
# pull out the two filter keys if present
domains = data.pop("filter_facet_domains", None)
ids = data.pop("filter_facet_ids", None)
# only add "filter" if at least one is set
if domains or ids:
filter_dict = {}
if domains is not None:
filter_dict["domains"] = domains
if ids is not None:
filter_dict["facet_ids"] = ids
data["filter"] = filter_dict
return data
class ResolveRequestPayload(BaseDCModel):
"""
A Pydantic model to structure, normalize, and validate the payload for a Resolve V2 API request.
Attributes:
node_dcids (str | list[str]): The DCID(s) of the nodes to query.
expression (str): The relation expression to query.
"""
node_dcids: ListOrStr = Field(..., serialization_alias="nodes")
expression: str | list[str] | None = Field(default=None,
serialization_alias="property")
resolver: str | None = Field(default=None, serialization_alias="resolver")
target: str | None = Field(default=None, serialization_alias="target")