Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Party Characteristics Tab and Visualizations to Dashboard #250

Merged
merged 53 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b0cb25a
Update party characteristics endpoints, add endpoints for calculating…
davidye007 Dec 13, 2024
628e29e
Fixed bug with getting a list of projects from a site. Fixed a bug th…
davidye007 Dec 13, 2024
c37cde3
Fixed bug with getting a list of projects from a site. Fixed a bug th…
davidye007 Dec 13, 2024
ee206a7
Convert SiteID feature in party characteristics data from String to L…
davidye007 Dec 13, 2024
f012e5a
add party characteristic average yearly statistics endpoints to autho…
davidye007 Dec 13, 2024
ed4985d
Add Party Characteristics Tab and Visualizations
davidye007 Dec 13, 2024
98e3fd7
fix linting error
davidye007 Dec 13, 2024
53f3ca0
Merge branch 'master' of https://github.com/OutdoorRD/trails-viz into…
davidye007 Dec 13, 2024
dc8c1ee
Successfully added color coded markers on top of each site. Markers a…
davidye007 Dec 16, 2024
bff38fc
change from color-coded markers to color-coded polygons
davidye007 Dec 17, 2024
8c0b42e
update api to handle data for chatbot response counts
davidye007 Dec 22, 2024
9523404
initial commit for visualizing chatbot locations and number of responses
davidye007 Dec 23, 2024
a79cc2f
added vuetify
davidye007 Dec 23, 2024
ac7f686
basic isolated range slider implementation, not integrated with map d…
davidye007 Dec 23, 2024
e110cd5
Initial commit for a vuex store approach to selecting and comparing s…
davidye007 Dec 25, 2024
8bdfc30
chatbot sites as an additional layer
davidye007 Dec 25, 2024
fedb207
single site layer approach update
davidye007 Dec 27, 2024
17ba0e4
Merge branch 'master' of https://github.com/OutdoorRD/trails-viz into…
davidye007 Dec 28, 2024
fea76b1
revert testing changes
davidye007 Dec 28, 2024
f534c56
Merge branch 'master' of https://github.com/OutdoorRD/trails-viz into…
davidye007 Dec 28, 2024
334e035
add chatbot activity layer function in progress
davidye007 Dec 28, 2024
0679bcd
functional chatbot map with year range selection
davidye007 Dec 29, 2024
c95e7b6
make hover and click work on maps for projects that don't have chatbo…
davidye007 Dec 29, 2024
5b5828a
data for a single feature returned per api request - a single api cal…
davidye007 Jan 6, 2025
9a38249
conditionally render chart of each featuere based on the status retur…
davidye007 Jan 6, 2025
50fff12
refactor api code into reusable helper functions, fix linting errors
davidye007 Jan 6, 2025
3d12a11
Update API from 'party_characteristics' -> 'chatbot_data' to support …
davidye007 Jan 6, 2025
48f8efd
minor bug fixes
davidye007 Jan 6, 2025
4137eff
Merge branch 'PartyCharacteristics' of https://github.com/OutdoorRD/t…
davidye007 Jan 7, 2025
fe031f8
fix all errors due to merge
davidye007 Jan 7, 2025
98e92b7
in-progress, show map based on selected sub-tab
davidye007 Jan 8, 2025
4afe0ed
implemented conditional rendering based on sub tab
davidye007 Jan 8, 2025
9c09418
Fixed 'portal teleportation' duplicate trailName bug by having select…
davidye007 Jan 8, 2025
08b5394
refactored site polygon reset (back to normal style) function, began …
davidye007 Jan 8, 2025
afce88b
implemented site highlighting based on search trail dropdown.
davidye007 Jan 8, 2025
8321187
fix error caused by undefined chatbotRes when user is not logged in.
davidye007 Jan 8, 2025
75e7251
enable hash siteid search (dropdown won't show anything, but polygon …
davidye007 Jan 12, 2025
3d6c4b1
refactoring functions in progress
davidye007 Jan 12, 2025
3041e7b
add chatbot map legend, chatbot response count to bubble tooltip
davidye007 Jan 14, 2025
61ba117
improve search by site id and accompanying site highlighting function…
davidye007 Jan 15, 2025
070d31e
polygons with duplicate trail names have site id appended to tooltip
davidye007 Jan 21, 2025
9b54446
make default polygon color slightly darker
davidye007 Jan 21, 2025
354d304
add year range label to slider
davidye007 Jan 21, 2025
92d88e8
show smaller bubbles on top of larger bubbles
davidye007 Jan 21, 2025
3256887
make copy of original data so dataframe modifications don't have last…
davidye007 Jan 28, 2025
5acdee3
Merge branch 'PartyCharacteristics' of https://github.com/OutdoorRD/t…
davidye007 Jan 28, 2025
6e13e85
Make a copy of df to isolate changes.
davidye007 Jan 28, 2025
258ffb2
Merge branch 'ChatbotLocations' of https://github.com/OutdoorRD/trail…
davidye007 Jan 28, 2025
b97d335
add initial Chatbot Home Locations and Chatbot Home Locations Map tab…
davidye007 Jan 28, 2025
a0639d6
PeoplePerVehics already available in dataset. Remove redundant calcul…
davidye007 Jan 28, 2025
ae94cc6
add InfoSource data prep and handle missing values
davidye007 Jan 28, 2025
a87ee16
fix linting errors
davidye007 Feb 3, 2025
329b662
release=major
davidye007 Feb 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

127 changes: 127 additions & 0 deletions trails-viz-api/trailsvizapi/repository/chatbot_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
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