def get_stats_wrapper(dcid_str, stats_var): """Wrapper function to get stats for multiple places and give statsvar. This wrapper takes concatenated place dcids as a string argument so the flask cache can work. Args: dcid_str: place dcids concatenated by "^". stats_var: the dcid of the statistical variable. Returns: An serialized json str. The json is an object keyed by the place dcid with value to be the observation time series. """ dcids = dcid_str.split('^') return json.dumps(dc.get_stats(dcids, stats_var))
def choropleth_values(): """Returns data for geographic subregions for a certain statistical variable. API Params: geoDcid: The currently viewed geography to render, as a string. level: The subgeographic level to pull and display information for, as a string. Choices: Country, AdministrativeLevel[1/2], City. statVar: The statistical variable to pull data about. API Returns: values: dictionary of geo to statistical variable values (as a float) for all subgeos. """ # Get required request parameters. requested_geoDcid = flask.request.args.get("geoDcid") if not requested_geoDcid: return flask.jsonify({"error": "Must provide a 'geoDcid' field!"}, 400) stat_var = flask.request.args.get("statVar") if not stat_var: return flask.jsonify({"error": "Must provide a 'statVar' field!"}, 400) display_level = get_sublevel(requested_geoDcid, flask.request.args.get("level")) if not display_level: return flask.jsonify( { "error": f"Failed to automatically resolve geographic subdivision level for" + f"{requested_geoDcid}. Please provide a 'level' field manually." }, 400) # Get all subgeos. geos_contained_in_place = dc.get_places_in([requested_geoDcid], display_level).get( requested_geoDcid, []) values_by_geo = dc.get_stats(geos_contained_in_place, stat_var) # Add to dictionary for response. populations_by_geo = {} for geo_id, payload in values_by_geo.items(): if payload and "data" in payload: latest_data = get_latest_data(payload) if latest_data: populations_by_geo[geo_id] = latest_data # Return as json payload. return flask.jsonify(populations_by_geo, 200)
def get_stats_wrapper(dcid_str, stats_var): """Wrapper function to get stats for multiple places and give statsvar. This wrapper takes concatenated place dcids as a string argument so the flask cache can work. Args: dcid_str: place dcids concatenated by "^". stats_var: the dcid of the statistical variable. Returns: An serialized json str. The json is an object keyed by the place dcid with value to be the observation time series. """ dcids = dcid_str.split('^') result = dc.get_stats(dcids, stats_var) for dcid in result: if not result[dcid]: # Convert {} to None so client side sees null instead of {} result[dcid] = None return json.dumps(result)
def choropleth_geo(): """Returns data for geographic subregions for a certain statistical variable. API Params: geoDcid: The currently viewed geography to render, as a string. level: The subgeographic level to pull and display information for, as a string. Choices: Country, AdministrativeLevel[1/2], City. mdom: The measurement denominator to use as a string. Defaults to "Count_Person". API Returns: geoJson: geoJson format that includes statistical variables info, geoDcid, and name for all subregions. """ # Get required request parameters. requested_geoDcid = flask.request.args.get("geoDcid") if not requested_geoDcid: return flask.jsonify({"error": "Must provide a 'geoDcid' field!"}, 400) display_level = get_sublevel(requested_geoDcid, flask.request.args.get("level")) geo_json_prop = GEOJSON_PROPERTY_MAP.get(display_level, None) if not display_level: return flask.jsonify( { "error": f"Failed to automatically resolve geographic subdivision level for" + f"{requested_geoDcid}. Please provide a 'level' field manually." }, 400) if not geo_json_prop: return flask.jsonify( { "error": f"Geojson data is not available for the geographic subdivision" + f"level needed for {requested_geoDcid}." }, 400) # Get optional fields. measurement_denominator = flask.request.args.get("mdom", default="Count_Person") # Get list of all contained places. geos_contained_in_place = dc.get_places_in([requested_geoDcid], display_level).get( requested_geoDcid, []) # Download statistical variable, names, and geojson for subgeos. # Also, handle the case where only a fraction of values are returned. names_by_geo = dc.get_property_values( geos_contained_in_place + [requested_geoDcid], "name") geojson_by_geo = dc.get_property_values(geos_contained_in_place, geo_json_prop) # Download population data if per capita. # TODO(iancostello): Determine how to handle populations # and statistical values from different times. population_by_geo = dc.get_stats(geos_contained_in_place, measurement_denominator) # Process into a combined json object. features = [] for geo_id, json_text in geojson_by_geo.items(): # Valid response needs at least geometry and a name. if not json_text: continue if geo_id not in names_by_geo: continue geo_feature = { "type": "Feature", "geometry": { "type": "MultiPolygon", }, "id": geo_id, "properties": { # Choose the first name when multiple are present. "name": names_by_geo.get(geo_id, ["Unnamed Area"])[0], "hasSublevel": (display_level in LEVEL_MAP), "geoDcid": geo_id, } } # Load, simplify, and add geoJSON coordinates. # Exclude geo if no or multiple renderings are present. if len(json_text) != 1: continue geojson = json.loads(json_text[0]) geo_feature['geometry']['coordinates'] = ( coerce_geojson_to_righthand_rule(geojson['coordinates'], geojson['type'])) if geo_id not in population_by_geo: continue population_payload = population_by_geo[geo_id] data = get_data(population_payload) # TODO(edumorales): return the population # for N date that all places have in common. if data: max_date = max(data) geo_feature["properties"]["pop"] = data[max_date] # Add to main dataframe. features.append(geo_feature) # Return as json payload. return flask.jsonify( { "type": "FeatureCollection", "features": features, "properties": { "current_geo": names_by_geo.get(requested_geoDcid, ["Unnamed Area"])[0] } }, 200)
def choropleth_geo(): """Returns data for geographic subregions for a certain statistical variable. API Params: geoDcid: The currently viewed geography to render, as a string. level: The subgeographic level to pull and display information for, as a string. Choices: Country, AdministrativeLevel[1/2], City. mdom: The measurement denominator to use as a string. Defaults to "Count_Person". API Returns: geoJson: geoJson format that includes statistical variables info, geoDcid, and name for all subregions. """ # Get required request parameters. requested_geoDcid = flask.request.args.get("geoDcid") if not requested_geoDcid: return flask.jsonify({"error": "Must provide a 'geoDcid' field!"}, 400) display_level = get_sublevel(requested_geoDcid, flask.request.args.get("level")) # Get optional fields. measurement_denominator = flask.request.args.get("mdom", default="Count_Person") # Get list of all contained places. # TODO(iancostello): Handle a failing function call. geos_contained_in_place = dc.get_places_in( [requested_geoDcid], display_level)[requested_geoDcid] # Download statistical variable, names, and geojson for subgeos. # TODO(iancostello): Handle failing function calls. # Also, handle the case where only a fraction of values are returned. names_by_geo = dc.get_property_values( geos_contained_in_place + [requested_geoDcid], "name") geojson_by_geo = dc.get_property_values(geos_contained_in_place, "geoJsonCoordinates") # Download population data if per capita. # TODO(iancostello): Determine how to handle populations # and statistical values from different times. population_by_geo = dc.get_stats(geos_contained_in_place, measurement_denominator) # Process into a combined json object. features, values = [], [] for geo_id, json_text in geojson_by_geo.items(): # Valid response needs at least geometry and a name. if json_text and geo_id in names_by_geo: geo_feature = { "type": "Feature", "geometry": { "type": "MultiPolygon", }, "id": geo_id, "properties": { # Choose the first name when multiple are present. # TODO(iancostello): Implement a better approach. "name": names_by_geo[geo_id][0], "hasSublevel": (display_level in LEVEL_MAP), "geoDcid": geo_id, } } # Load, simplify, and add geoJSON coordinates. # First returned value is chosen always. # TODO(iancostello): Implement a better approach. geojson = json.loads(json_text[0]) geo_feature['geometry']['coordinates'] = ( coerce_geojson_to_righthand_rule(geojson['coordinates'], geojson['type'])) # Process Statistical Observation if valid. if ('data' in population_by_geo.get(geo_id, [])): # Grab the latest available data. geo_feature["properties"]["pop"] = next( iter(reversed(population_by_geo[geo_id]['data'].values()))) # Add to main dataframe. features.append(geo_feature) # Return as json payload. return flask.jsonify( { "type": "FeatureCollection", "features": features, "properties": { # TODO(iancostello): Don't just pick the first and check. "current_geo": names_by_geo[requested_geoDcid][0] } }, 200)
def get_stats_wrapper(dcid_str, stats_var): dcids = dcid_str.split('^') return json.dumps(dc.get_stats(dcids, stats_var))