Skip to content

Commit 0ebdf0b

Browse files
authored
Improvements suggested by kara (#234)
-removes `fetch_latest_observations` as it didn’t really serve a useful role -removes`fetch_latest_observations_by_entity` as it is basically the same as `fetch_observations_by_entity` but just with a default of the latest date. - renames`fetch_observations_by_entity` to `fetch_observations_by_entity_dcid` - renames`get_observations_as_records` to `to_observation_records`. This name more accurately reflects what the method does: it transforms nested observation data and facet metadata into a list of flat, record-like dictionaries suitable for analysis. This mirrors common data science terminology.
1 parent 709abc7 commit 0ebdf0b

6 files changed

Lines changed: 28 additions & 149 deletions

File tree

datacommons_client/client.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,11 @@ def _find_filter_facet_ids(
8787
return None
8888

8989
if fetch_by == "entity":
90-
observations = self.observation.fetch_observations_by_entity(
90+
observations = self.observation.fetch_observations_by_entity_dcid(
9191
date=date,
9292
entity_dcids=entity_dcids,
9393
variable_dcids=variable_dcids,
94-
select=["variable", "entity", "facet"],
95-
)
94+
select=["variable", "entity", "facet"])
9695
else:
9796
observations = self.observation.fetch_observations_by_entity_type(
9897
date=date,
@@ -176,11 +175,10 @@ def observations_dataframe(
176175
filter_facet_ids=facets,
177176
)
178177
else:
179-
observations = self.observation.fetch_observations_by_entity(
178+
observations = self.observation.fetch_observations_by_entity_dcid(
180179
date=date,
181180
entity_dcids=entity_dcids,
182181
variable_dcids=variable_dcids,
183-
filter_facet_ids=facets,
184-
)
182+
filter_facet_ids=facets)
185183

186-
return pd.DataFrame(observations.get_observations_as_records())
184+
return pd.DataFrame(observations.to_observation_records())

datacommons_client/endpoints/observation.py

Lines changed: 2 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -63,72 +63,6 @@ def fetch(
6363
# Send the request
6464
return ObservationResponse.from_json(self.post(payload))
6565

66-
def fetch_latest_observations(
67-
self,
68-
variable_dcids: str | list[str],
69-
entity_dcids: Optional[str | list[str]] = None,
70-
entity_expression: Optional[str] = None,
71-
*,
72-
select: Optional[list[ObservationSelect | str]] = None,
73-
filter_facet_domains: Optional[str | list[str]] = None,
74-
filter_facet_ids: Optional[str | list[str]] = None,
75-
) -> ObservationResponse:
76-
"""
77-
Fetches the latest observations for the given variable and entity.
78-
79-
Args:
80-
variable_dcids (str | list[str]): One or more variable IDs for the data.
81-
entity_dcids (Optional[str | list[str]]): One or more entity IDs to filter the data.
82-
entity_expression (Optional[str]): A string expression to filter entities.
83-
select (Optional[list[ObservationSelect | str]]): Fields to include in the response.
84-
If not provided, defaults to ["date", "variable", "entity", "value"].
85-
filter_facet_domains: Optional[str | list[str]: One or more domain names to filter the data.
86-
filter_facet_ids: Optional[str | list[str]: One or more facet IDs to filter the data.
87-
88-
Returns:
89-
ObservationResponse: The response object containing observations for the specified query.
90-
"""
91-
return self.fetch(
92-
variable_dcids=variable_dcids,
93-
date=ObservationDate.LATEST,
94-
entity_dcids=entity_dcids,
95-
entity_expression=entity_expression,
96-
filter_facet_domains=filter_facet_domains,
97-
filter_facet_ids=filter_facet_ids,
98-
select=[s for s in ObservationSelect] if not select else select,
99-
)
100-
101-
def fetch_latest_observations_by_entity(
102-
self,
103-
variable_dcids: str | list[str],
104-
entity_dcids: str | list[str],
105-
*,
106-
select: Optional[list[ObservationSelect | str]] = None,
107-
filter_facet_domains: Optional[str | list[str]] = None,
108-
filter_facet_ids: Optional[str | list[str]] = None,
109-
) -> ObservationResponse:
110-
"""Fetches the latest observations for the given variable and entities.
111-
112-
Args:
113-
variable_dcids (str | list[str]): One or more variable IDs for the data.
114-
entity_dcids (str | list[str]): One or more entity IDs to filter the data.
115-
select (Optional[list[ObservationSelect | str]]): Fields to include in the response.
116-
If not provided, defaults to ["date", "variable", "entity", "value"].
117-
filter_facet_domains: Optional[str | list[str]: One or more domain names to filter the data.
118-
filter_facet_ids: Optional[str | list[str]: One or more facet IDs to filter the data.
119-
120-
Returns:
121-
ObservationResponse: The response object containing observations for the specified query.
122-
"""
123-
124-
return self.fetch_latest_observations(
125-
variable_dcids=variable_dcids,
126-
entity_dcids=entity_dcids,
127-
select=[s for s in ObservationSelect] if not select else select,
128-
filter_facet_domains=filter_facet_domains,
129-
filter_facet_ids=filter_facet_ids,
130-
)
131-
13266
def fetch_observations_by_entity_type(
13367
self,
13468
date: ObservationDate | str,
@@ -185,7 +119,7 @@ def fetch_observations_by_entity_type(
185119
filter_facet_ids=filter_facet_ids,
186120
)
187121

188-
def fetch_observations_by_entity(
122+
def fetch_observations_by_entity_dcid(
189123
self,
190124
date: ObservationDate | str,
191125
entity_dcids: str | list[str],
@@ -218,7 +152,7 @@ def fetch_observations_by_entity(
218152
219153
```python
220154
api = API()
221-
ObservationEndpoint(api).fetch_observations_by_entity(
155+
ObservationEndpoint(api).fetch_observations_by_entity_dcid(
222156
date="all",
223157
entity_dcids="country/NGA",
224158
variable_dcids="sdg/SI_POV_DAY1"

datacommons_client/endpoints/response.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,20 @@ def get_data_by_entity(self) -> Dict:
105105
variable: data.byEntity for variable, data in self.byVariable.items()
106106
}
107107

108-
def get_observations_as_records(self) -> List[Dict[str, Any]]:
109-
"""Converts the observation data into a list of records.
108+
def to_observation_records(self) -> List[Dict[str, Any]]:
109+
"""Returns a flat list of observation records combining date, variable, entity,
110+
observation, and facet metadata.
110111
111-
Returns:
112-
List[Dict[str, Any]]: A flattened list of observation records.
112+
This method transforms the nested `byVariable` and `facets` data in the ObservationResponse
113+
into a flat list of dictionaries. Each dictionary (or "record") represents a single observation
114+
for a variable and entity, enriched with its associated facet metadata (e.g., measurement method,
115+
observation period, unit).
116+
117+
This format is suitable for exporting to a DataFrame or serializing to JSON for tabular or analytical use.
118+
119+
Returns:
120+
List[Dict[str, Any]]: A list of observation records, where each record contains the variable,
121+
entity, date, value, facetId, and any additional metadata provided by the facet.
113122
"""
114123
return observations_as_records(data=self.get_data_by_entity(),
115124
facets=self.facets)

datacommons_client/tests/endpoints/test_observation_endpoint.py

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -42,68 +42,6 @@ def test_fetch():
4242
next_token=None)
4343

4444

45-
def test_fetch_latest_observation():
46-
"""Tests the fetch_latest_observation method."""
47-
api_mock = MagicMock(spec=API)
48-
endpoint = ObservationEndpoint(api=api_mock)
49-
50-
response = endpoint.fetch_latest_observations(
51-
variable_dcids=["dc/Variable1", "dc/Variable2"],
52-
select=["date", "variable", "entity", "value"],
53-
entity_dcids="dc/EntityID")
54-
55-
# Check the response
56-
assert isinstance(response, ObservationResponse)
57-
58-
# Check the post request
59-
api_mock.post.assert_called_once_with(payload={
60-
"date": ObservationDate.LATEST,
61-
"variable": {
62-
"dcids": ["dc/Variable1", "dc/Variable2"]
63-
},
64-
"entity": {
65-
"dcids": ["dc/EntityID"]
66-
},
67-
"select": ["date", "variable", "entity", "value"],
68-
},
69-
endpoint="observation",
70-
all_pages=True,
71-
next_token=None)
72-
73-
74-
def test_fetch_latest_observations_by_entity():
75-
"""Tests the fetch_latest_observations_by_entity method."""
76-
api_mock = MagicMock(spec=API)
77-
endpoint = ObservationEndpoint(api=api_mock)
78-
79-
response = endpoint.fetch_latest_observations_by_entity(
80-
variable_dcids="dc/VariableID",
81-
entity_dcids=["dc/Entity1", "dc/Entity2"],
82-
select=["date", "variable", "entity", "value"],
83-
filter_facet_ids="facet1")
84-
85-
# Check the response
86-
assert isinstance(response, ObservationResponse)
87-
88-
# Check the post request
89-
api_mock.post.assert_called_once_with(payload={
90-
"date": ObservationDate.LATEST,
91-
"variable": {
92-
"dcids": ["dc/VariableID"]
93-
},
94-
"entity": {
95-
"dcids": ["dc/Entity1", "dc/Entity2"]
96-
},
97-
"select": ["date", "variable", "entity", "value"],
98-
"filter": {
99-
"facet_ids": ["facet1"]
100-
}
101-
},
102-
endpoint="observation",
103-
all_pages=True,
104-
next_token=None)
105-
106-
10745
def test_fetch_observations_by_entity_type():
10846
"""Tests the fetch_observations_by_entity_type method."""
10947
api_mock = MagicMock(spec=API)

datacommons_client/tests/endpoints/test_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ def test_get_observations_as_records():
488488
response = ObservationResponse(byVariable=mock_data, facets=mock_facets)
489489

490490
# Call the method and get the result
491-
result = response.get_observations_as_records()
491+
result = response.to_observation_records()
492492

493493
# Expected output
494494
expected = [

datacommons_client/tests/test_client.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def test_observations_dataframe_raises_error_when_invalid_entity_type_usage(
9696
def test_observations_dataframe_calls_fetch_observations_by_entity_type(
9797
mock_client):
9898
"""Tests that fetch_observations_by_entity_type is called with correct parameters."""
99-
mock_client.observation.fetch_observations_by_entity_type.return_value.get_observations_as_records.return_value = (
99+
mock_client.observation.fetch_observations_by_entity_type.return_value.to_observation_records.return_value = (
100100
[])
101101

102102
df = mock_client.observations_dataframe(
@@ -122,14 +122,14 @@ def test_observations_dataframe_calls_fetch_observations_by_entity_type(
122122
def test_observations_dataframe_calls_fetch_observations_by_entity(mock_client):
123123
"""Tests that fetch_observations_by_entity is called with correct parameters."""
124124

125-
mock_client.observation.fetch_observations_by_entity.return_value.get_observations_as_records.return_value = (
125+
mock_client.observation.fetch_observations_by_entity_dcid.return_value.to_observation_records.return_value = (
126126
[])
127127

128128
df = mock_client.observations_dataframe(variable_dcids="var1",
129129
date="latest",
130130
entity_dcids=["entity1", "entity2"])
131131

132-
mock_client.observation.fetch_observations_by_entity.assert_called_once_with(
132+
mock_client.observation.fetch_observations_by_entity_dcid.assert_called_once_with(
133133
date="latest",
134134
entity_dcids=["entity1", "entity2"],
135135
variable_dcids="var1",
@@ -142,7 +142,7 @@ def test_observations_dataframe_calls_fetch_observations_by_entity(mock_client):
142142
def test_observations_dataframe_returns_dataframe_with_expected_columns(
143143
mock_client):
144144
"""Tests that the method returns a DataFrame with expected columns."""
145-
mock_client.observation.fetch_observations_by_entity.return_value.get_observations_as_records.return_value = [
145+
mock_client.observation.fetch_observations_by_entity_dcid.return_value.to_observation_records.return_value = [
146146
{
147147
"date": "2024",
148148
"entity": "entity1",
@@ -198,7 +198,7 @@ def test_find_filter_facet_ids_returns_none_when_no_filters(mock_client):
198198

199199
def test_find_filter_facet_ids_returns_facet_ids(mock_client):
200200
"""Tests that _find_filter_facet_ids correctly returns facet IDs when filters are provided."""
201-
mock_client.observation.fetch_observations_by_entity.return_value.find_matching_facet_id.side_effect = [
201+
mock_client.observation.fetch_observations_by_entity_dcid.return_value.find_matching_facet_id.side_effect = [
202202
["213"], ["3243"]
203203
]
204204

@@ -219,7 +219,7 @@ def test_observations_dataframe_filters_by_facet_ids(mock_client):
219219
"""Tests that observations_dataframe includes facet filtering when property_filters are used."""
220220
mock_client._find_filter_facet_ids = MagicMock(
221221
return_value=["facet_1", "facet_2"])
222-
mock_client.observation.fetch_observations_by_entity.return_value.get_observations_as_records.return_value = []
222+
mock_client.observation.fetch_observations_by_entity_dcid.return_value.to_observation_records.return_value = []
223223

224224
df = mock_client.observations_dataframe(
225225
variable_dcids="var1",
@@ -228,7 +228,7 @@ def test_observations_dataframe_filters_by_facet_ids(mock_client):
228228
property_filters={"measurementMethod": "Census"},
229229
)
230230

231-
mock_client.observation.fetch_observations_by_entity.assert_called_once_with(
231+
mock_client.observation.fetch_observations_by_entity_dcid.assert_called_once_with(
232232
variable_dcids="var1",
233233
date="2024",
234234
entity_dcids=["entity1"],

0 commit comments

Comments
 (0)