diff --git a/packages/client/src/data_commons_web_client_types.ts b/packages/client/src/data_commons_web_client_types.ts index 6300805272..bb20c63a4c 100644 --- a/packages/client/src/data_commons_web_client_types.ts +++ b/packages/client/src/data_commons_web_client_types.ts @@ -186,4 +186,5 @@ export interface RelatedPlacesApiResponse { nearbyPlaces: Place[]; place: Place; similarPlaces: Place[]; + parentPlaces: Place[]; } diff --git a/server/routes/dev_place/api.py b/server/routes/dev_place/api.py index 515f83b280..882d4456e5 100644 --- a/server/routes/dev_place/api.py +++ b/server/routes/dev_place/api.py @@ -150,6 +150,7 @@ def related_places(place_dcid: str): child_place_dcids = place_utils.fetch_child_place_dcids(place, child_place_type, locale=g.locale) + parent_places = place_utils.get_parent_places(place.dcid) # Fetch all place objects in one request to reduce latency (includes name and typeOf) all_place_dcids = [ @@ -163,11 +164,10 @@ def related_places(place_dcid: str): similar_places = [all_place_by_dcid[dcid] for dcid in similar_place_dcids] child_places = [all_place_by_dcid[dcid] for dcid in child_place_dcids] - response = RelatedPlacesApiResponse( - childPlaceType=child_place_type, - childPlaces=child_places, - nearbyPlaces=nearby_places, - place=place, - similarPlaces=similar_places, - ) + response = RelatedPlacesApiResponse(childPlaceType=child_place_type, + childPlaces=child_places, + nearbyPlaces=nearby_places, + place=place, + similarPlaces=similar_places, + parentPlaces=parent_places) return jsonify(response) diff --git a/server/routes/dev_place/types.py b/server/routes/dev_place/types.py index a14bf7211b..4cdedbf45b 100644 --- a/server/routes/dev_place/types.py +++ b/server/routes/dev_place/types.py @@ -68,3 +68,4 @@ class RelatedPlacesApiResponse: nearbyPlaces: List[Place] place: Place similarPlaces: List[Place] + parentPlaces: List[Place] = None diff --git a/server/routes/dev_place/utils.py b/server/routes/dev_place/utils.py index d4365da230..9d88185a49 100644 --- a/server/routes/dev_place/utils.py +++ b/server/routes/dev_place/utils.py @@ -53,6 +53,25 @@ def get_place_html_link(place_dcid: str, place_name: str) -> str: return f'{place_name}' +def get_parent_places(dcid: str) -> List[Place]: + """Gets the parent places for a given DCID + + Args: + dcid: dcid of the place to get parents for + + Returns: + A list of places that are all the parents of the given DCID. + """ + parents_resp = place_api.parent_places([dcid], include_admin_areas=True).get( + dcid, []) + all_parents = [] + for parent in parents_resp: + all_parents.append( + Place(dcid=parent['dcid'], name=parent['name'], types=parent['type'])) + + return all_parents + + def get_place_type_with_parent_places_links(dcid: str) -> str: """Get ' in ' with html links for a given DCID @@ -68,28 +87,27 @@ def get_place_type_with_parent_places_links(dcid: str) -> str: place_type_display_name = place_api.get_place_type_i18n_name(place_type) # Get parent places - all_parents = place_api.parent_places([dcid], - include_admin_areas=True).get(dcid, []) + all_parents = get_parent_places(dcid) # Filter parents to only the types desired parents_to_include = [ parent for parent in all_parents - if parent['type'] in PARENT_PLACE_TYPES_TO_HIGHLIGHT + if parent.types in PARENT_PLACE_TYPES_TO_HIGHLIGHT ] # Fetch the localized names of the parents - parent_dcids = [parent['dcid'] for parent in parents_to_include] + parent_dcids = [parent.dcid for parent in parents_to_include] localized_names = place_api.get_i18n_name(parent_dcids) places_with_names = [ parent for parent in parents_to_include - if parent['dcid'] in localized_names.keys() + if parent.dcid in localized_names.keys() ] # Generate tag for each parent place links = [ - get_place_html_link(place_dcid=parent['dcid'], - place_name=localized_names.get(parent['dcid'])) - if parent['type'] != 'Continent' else localized_names.get(parent['dcid']) + get_place_html_link(place_dcid=parent.dcid, + place_name=localized_names.get(parent.dcid)) + if parent.types != 'Continent' else localized_names.get(parent.dcid) for parent in places_with_names ] diff --git a/server/tests/routes/api/dev_place_test.py b/server/tests/routes/api/dev_place_test.py index b16ee71151..c590bdb2cd 100644 --- a/server/tests/routes/api/dev_place_test.py +++ b/server/tests/routes/api/dev_place_test.py @@ -92,12 +92,13 @@ def test_dev_place_charts(self, mock_obs_point_within, mock_obs_point, self.assertEqual(5, len(response_json["charts"][2]["denominator"])) self.assertEqual(5, len(response_json["charts"][3]["denominator"])) + @patch('server.routes.shared_api.place.parent_places') @patch('server.routes.dev_place.utils.fetch.raw_property_values') @patch('server.routes.dev_place.utils.fetch.multiple_property_values') @patch('server.routes.dev_place.utils.fetch.descendent_places') def test_related_places(self, mock_descendent_places, mock_multiple_property_values, - mock_raw_property_values): + mock_raw_property_values, mock_parent_places): """Test the /api/dev-place/related-places endpoint. Mocks fetch.* and dc.* calls.""" with app.app_context(): @@ -139,6 +140,18 @@ def mock_raw_property_values_side_effect(nodes, prop, out): mock_raw_property_values.side_effect = mock_raw_property_values_side_effect + # Define side effects for mock_parent_places_ + def mock_parent_places_side_effect(dcids, include_admin_areas): + return { + 'dcid': 'geoId/06', + 'parents': [{ + 'type': 'Country', + 'dcid': 'country/USA' + }] + } + + mock_parent_places.side_effect = mock_parent_places_side_effect + # Send a GET request to the related-places endpoint response = app.test_client().get( f'/api/dev-place/related-places/{place_dcid}') @@ -155,6 +168,7 @@ def mock_raw_property_values_side_effect(nodes, prop, out): self.assertIn('nearbyPlaces', response_json) self.assertIn('place', response_json) self.assertIn('similarPlaces', response_json) + self.assertIn('parentPlaces', response_json) # Check the place field self.assertEqual(response_json['place']['dcid'], place_dcid) @@ -186,12 +200,14 @@ def mock_raw_property_values_side_effect(nodes, prop, out): # Check the 'childPlaceType' field self.assertEqual(response_json['childPlaceType'], "State") + @patch('server.routes.shared_api.place.parent_places') @patch('server.routes.dev_place.utils.fetch.raw_property_values') @patch('server.routes.dev_place.utils.fetch.multiple_property_values') @patch('server.routes.dev_place.utils.fetch.descendent_places') def test_related_places_es_locale(self, mock_descendent_places, mock_multiple_property_values, - mock_raw_property_values): + mock_raw_property_values, + mock_parent_places): """Test the /api/dev-place/related-places endpoint with 'es' locale.""" with app.app_context(): # Sample place_dcid @@ -267,6 +283,18 @@ def mock_raw_property_values_side_effect(nodes, prop, out): mock_raw_property_values.side_effect = mock_raw_property_values_side_effect + # Define side effects for mock_parent_places_ + def mock_parent_places_side_effect(dcids, include_admin_areas): + return { + 'dcid': 'geoId/06', + 'parents': [{ + 'type': 'Country', + 'dcid': 'country/USA' + }] + } + + mock_parent_places.side_effect = mock_parent_places_side_effect + mock_descendent_places.return_value = { 'country/USA': ['geoId/06', 'geoId/07'] } diff --git a/static/js/place/dev_place_main.tsx b/static/js/place/dev_place_main.tsx index ec17c84871..ce5772a005 100644 --- a/static/js/place/dev_place_main.tsx +++ b/static/js/place/dev_place_main.tsx @@ -37,6 +37,7 @@ import { defaultDataCommonsClient, defaultDataCommonsWebClient, } from "../utils/data_commons_client"; +import { isPlaceContainedInUsa } from "./util"; /** * Returns the stat var key for a chart. @@ -355,8 +356,12 @@ const PlaceOverviewTable = (props: { placeDcid: string }) => { const PlaceOverview = (props: { place: NamedTypedPlace; placeSummary: string; + parentPlaces: NamedTypedPlace[]; }) => { - const { place, placeSummary } = props; + const { place, placeSummary, parentPlaces } = props; + const isInUsa = isPlaceContainedInUsa( + parentPlaces.map((place) => place.dcid) + ); return (
@@ -365,10 +370,13 @@ const PlaceOverview = (props: {
{place.name}
{placeSummary}
-
- -
+ {isInUsa && ( +
+ +
+ )}
+ {!isInUsa &&

}
@@ -454,6 +462,7 @@ export const DevPlaceMain = () => { // Derived place data const [childPlaceType, setChildPlaceType] = useState(); const [childPlaces, setChildPlaces] = useState([]); + const [parentPlaces, setParentPlaces] = useState([]); const [pageConfig, setPageConfig] = useState(); const urlParams = new URLSearchParams(window.location.search); @@ -505,6 +514,7 @@ export const DevPlaceMain = () => { ); setChildPlaceType(relatedPlacesApiResponse.childPlaceType); setChildPlaces(relatedPlacesApiResponse.childPlaces); + setParentPlaces(relatedPlacesApiResponse.parentPlaces); setPageConfig(pageConfig); })(); }, [place]); @@ -520,7 +530,11 @@ export const DevPlaceMain = () => { placeSubheader={placeSubheader} /> - + {place && pageConfig && (