Skip to content

Commit

Permalink
Merge pull request #250 from OutdoorRD/PartyCharacteristics
Browse files Browse the repository at this point in the history
Added Party Characteristics Tab and Visualizations to Dashboard
  • Loading branch information
davidye007 authored Feb 4, 2025
2 parents 801f6f0 + 329b662 commit e84d630
Show file tree
Hide file tree
Showing 26 changed files with 11,364 additions and 19,104 deletions.
2,249 changes: 2,249 additions & 0 deletions trails-viz-api/package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions trails-viz-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"@turf/turf": "^7.1.0"
}
}
3 changes: 2 additions & 1 deletion trails-viz-api/trailsvizapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
from trailsvizapi.controller.home_locations import *
from trailsvizapi.controller.visitation import *
from trailsvizapi.controller.users_controller import *
from trailsvizapi.controller.party_characteristics import *
from trailsvizapi.controller.chatbot_data import *
from trailsvizapi.controller.map_chatbots import *
14 changes: 12 additions & 2 deletions trails-viz-api/trailsvizapi/config/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,18 @@
'get_project_home_locations_demographics',
'get_user',
'update_user',
'get_project_party_characteristics',
'get_party_characteristics'
'get_project_chatbot_data',
'get_chatbot_data',
'get_project_chatbot_data_yearly_statistics',
'get_chatbot_data_yearly_statistics',
'get_annual_chatbot_response_counts',
'get_chatbot_project_home_locations',
'get_chatbot_home_locations',
'get_chatbot_home_locations_state',
'get_chatbot_home_locations_county',
'get_chatbot_project_home_locations_state',
'get_chatbot_home_project_locations_county',

])

_ROLE_ACCESS_MAPPING = {
Expand Down
22 changes: 22 additions & 0 deletions trails-viz-api/trailsvizapi/controller/chatbot_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from trailsvizapi import app
from trailsvizapi.repository import chatbot_data


@app.route('/api/projects/<string:project>/chatbotData/<string:characteristic>')
def get_project_chatbot_data(project, characteristic):
return chatbot_data.get_project_chatbot_data(project, characteristic)


@app.route('/api/sites/<string:siteid>/chatbotData/<string:characteristic>')
def get_chatbot_data(siteid, characteristic):
return chatbot_data.get_chatbot_data(siteid, characteristic)


@app.route('/api/projects/<string:project>/chatbotDataYearlyStatistics/<string:characteristic>')
def get_project_chatbot_data_yearly_statistics(project, characteristic):
return chatbot_data.get_project_chatbot_data_yearly_statistics(project, characteristic)


@app.route('/api/sites/<string:siteid>/chatbotDataYearlyStatistics/<string:characteristic>')
def get_chatbot_data_yearly_statistics(siteid, characteristic):
return chatbot_data.get_chatbot_data_yearly_statistics(siteid, characteristic)
36 changes: 36 additions & 0 deletions trails-viz-api/trailsvizapi/controller/home_locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,36 @@ def get_home_locations(siteid):
return jsonify(data)


@app.route('/api/sites/<string:siteid>/chatbotHomeLocations')
def get_chatbot_home_locations(siteid):
data = home_locations.get_chatbot_home_locations(siteid)
return jsonify(data)


@app.route('/api/sites/<string:siteid>/homeLocationsState')
def get_home_locations_state(siteid):
data = home_locations.get_home_locations_by_state(siteid)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/sites/<string:siteid>/chatbotHomeLocationsState')
def get_chatbot_home_locations_state(siteid):
data = home_locations.get_chatbot_home_locations_by_state(siteid)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/sites/<string:siteid>/homeLocationsCounty/<string:state_code>')
def get_home_locations_county(siteid, state_code):
data = home_locations.get_home_locations_by_county(siteid, state_code)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/sites/<string:siteid>/chatbotHomeLocationsCounty/<string:state_code>')
def get_chatbot_home_locations_county(siteid, state_code):
data = home_locations.get_chatbot_home_locations_by_county(siteid, state_code)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/sites/<string:siteid>/homeLocationsCensusTract/<string:state_code>/<string:county_code>')
def get_home_locations_census_tract(siteid, state_code, county_code):
data = home_locations.get_home_locations_by_census_tract(siteid, state_code, county_code)
Expand All @@ -34,18 +52,36 @@ def get_project_home_locations(project):
return jsonify(data)


@app.route('/api/projects/<string:project>/chatbotHomeLocations')
def get_chatbot_project_home_locations(project):
data = home_locations.get_chatbot_project_home_locations(project)
return jsonify(data)


@app.route('/api/projects/<string:project>/homeLocationsState')
def get_project_home_locations_state(project):
data = home_locations.get_project_home_locations_by_state(project)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/projects/<string:project>/chatbotHomeLocationsState')
def get_chatbot_project_home_locations_state(project):
data = home_locations.get_chatbot_project_home_locations_by_state(project)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/projects/<string:project>/homeLocationsCounty/<string:state_code>')
def get_home_project_locations_county(project, state_code):
data = home_locations.get_project_home_locations_by_county(project, state_code)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/projects/<string:project>/chatbotHomeLocationsCounty/<string:state_code>')
def get_chatbot_home_project_locations_county(project, state_code):
data = home_locations.get_chatbot_project_home_locations_by_county(project, state_code)
return Response(data.to_json(), mimetype='application/json')


@app.route('/api/projects/<string:project>/homeLocationsCensusTract/<string:state_code>/<string:county_code>')
def get_project_home_locations_census_tract(project, state_code, county_code):
data = home_locations.get_project_home_locations_by_census_tract(project, state_code, county_code)
Expand Down
10 changes: 10 additions & 0 deletions trails-viz-api/trailsvizapi/controller/map_chatbots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from flask import Response
from trailsvizapi import app
from trailsvizapi.repository import map_chatbots
import json


@app.route('/api/projects/<string:project>/get_annual_chatbot_response_counts')
def get_annual_chatbot_response_counts(project):
data = map_chatbots.get_annual_chatbot_response_counts(project)
return Response(json.dumps(data), mimetype='application/json')
15 changes: 0 additions & 15 deletions trails-viz-api/trailsvizapi/controller/party_characteristics.py

This file was deleted.

128 changes: 128 additions & 0 deletions trails-viz-api/trailsvizapi/repository/chatbot_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from flask import Response, jsonify
from trailsvizapi.repository.prepare_data import get_from_data_source
from trailsvizapi.repository.projects_and_sites import get_project_sites
import pandas as pd
import numpy as np


CONFIDENCE_LEVEL = 1.96


def get_project_chatbot_data(project, characteristic):
chatbot_data_df_long = _prep_chatbot_project_long_df(project=project, characteristic=characteristic)
if chatbot_data_df_long is None:
return Response(status=204)
response_data = chatbot_data_df_long.to_json(orient='records')
return Response(response_data, status=200, mimetype='application/json')


def get_chatbot_data(siteid, characteristic):
chatbot_data_df_long = _prep_chatbot_site_long_df(siteid=siteid, characteristic=characteristic)
if chatbot_data_df_long is None:
return Response(status=204)
response_data = chatbot_data_df_long.to_json(orient='records')
return Response(response_data, status=200, mimetype='application/json')


def get_project_chatbot_data_yearly_statistics(project, characteristic):
chatbot_data_df_long = _prep_chatbot_project_long_df(project=project, characteristic=characteristic)
aggregate_data = _prep_chatbot_aggregate_stats(df_long=chatbot_data_df_long,
characteristic=characteristic)
if aggregate_data is None:
return Response(status=204)
return jsonify(aggregate_data), 200


def get_chatbot_data_yearly_statistics(siteid, characteristic):
chatbot_data_df_long = _prep_chatbot_site_long_df(siteid=siteid, characteristic=characteristic)
aggregate_data = _prep_chatbot_aggregate_stats(df_long=chatbot_data_df_long,
characteristic=characteristic)
if aggregate_data is None:
return Response(status=204)
return jsonify(aggregate_data), 200


def _prep_chatbot_project_long_df(project, characteristic):
project_sites = get_project_sites(project)
siteids = set(project_sites['siteid'].unique())
chatbot_data_df = get_from_data_source('CHATBOT_DATA_DF').copy()
# Add a 'trail' feature that contains the project assoicated site id.
# 'trail' value set to None if no site ids in 'SiteID' are not included in the list of project siteids.
chatbot_data_df['trail'] = chatbot_data_df['SiteID'].apply(
lambda lst: next((x for x in lst if x in siteids), None))
# keep observations with non-missing 'trail' values
chatbot_data_df = chatbot_data_df.dropna(subset=['trail'])
if chatbot_data_df.empty:
return None
if characteristic not in chatbot_data_df.columns:
chatbot_data_df = _add_additional_features(chatbot_data_df, characteristic)
if characteristic not in chatbot_data_df.columns:
return None
# add year feature
chatbot_data_df.loc[:, 'year'] = pd.to_datetime(chatbot_data_df['date']).dt.year
chatbot_data_df_long = chatbot_data_df.melt(
id_vars=['date', 'year', 'trail'],
value_vars=[characteristic],
var_name='characteristic',
value_name='value'
)
chatbot_data_df_long = chatbot_data_df_long.dropna(subset=['date', 'year', 'trail', 'value'])
if chatbot_data_df_long.empty:
return None
return chatbot_data_df_long


def _prep_chatbot_site_long_df(siteid, characteristic):
chatbot_data_df = get_from_data_source('CHATBOT_DATA_DF').copy()
# Add a 'trail' feature that contains the given siteid.
# 'trail' value set to None if 'SiteID' does not include siteid.
chatbot_data_df['trail'] = chatbot_data_df['SiteID'].apply(
lambda x: siteid if siteid in x else None)
# keep observations with non-missing 'trail' values
chatbot_data_df = chatbot_data_df.dropna(subset=['trail'])
if chatbot_data_df.empty:
return None
if characteristic not in chatbot_data_df.columns:
chatbot_data_df = _add_additional_features(chatbot_data_df, characteristic)
if characteristic not in chatbot_data_df.columns:
return None
# add year feature
chatbot_data_df.loc[:, 'year'] = pd.to_datetime(chatbot_data_df['date']).dt.year
chatbot_data_df_long = chatbot_data_df.melt(
id_vars=['date', 'year', 'trail'],
value_vars=[characteristic],
var_name='characteristic',
value_name='value'
)
chatbot_data_df_long = chatbot_data_df_long.dropna(subset=['date', 'year', 'trail', 'value'])
if chatbot_data_df_long.empty:
return None
return chatbot_data_df_long


def _prep_chatbot_aggregate_stats(df_long, characteristic):
if df_long is None:
return None
grouped = (
df_long.groupby(['year', 'characteristic'])
.agg(mean=('value', 'mean'), std=('value', 'std'), count=('value', 'count'))
.reset_index()
)
if grouped.empty:
return None
grouped = grouped.sort_values(by='year')
grouped['upper'] = grouped['mean'] + CONFIDENCE_LEVEL * grouped['std'] / np.sqrt(grouped['count'])
grouped['lower'] = grouped['mean'] - CONFIDENCE_LEVEL * grouped['std'] / np.sqrt(grouped['count'])
response_data = {
"years": grouped['year'].unique().tolist(),
characteristic: {
"mean": grouped.loc[grouped['characteristic'] == characteristic, 'mean'].round(2).tolist(),
"upper_bound": grouped.loc[grouped['characteristic'] == characteristic, 'upper'].round(2).tolist(),
"lower_bound": grouped.loc[grouped['characteristic'] == characteristic, 'lower'].round(2).tolist()
}
}
return response_data


def _add_additional_features(df, feature):
pass
Loading

0 comments on commit e84d630

Please sign in to comment.