Skip to content

Commit

Permalink
[ENH] Make the dash app load the images
Browse files Browse the repository at this point in the history
This also means the app won't work if the images are not there
  • Loading branch information
surchs committed Sep 25, 2024
1 parent 2a9c2d3 commit abd9d8c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 23 deletions.
28 changes: 13 additions & 15 deletions climate_emotions_map/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
from dash.exceptions import PreventUpdate

from . import utility as utils
from .data_loader import NATIONAL_SAMPLE_SIZE, SURVEY_DATA
from .data_loader import (
NATIONAL_SAMPLE_SIZE,
PRERENDERED_BARPLOTS,
SURVEY_DATA,
)
from .layout import MAP_LAYOUT, SINGLE_SUBQUESTION_FIG_KW, construct_layout
from .make_descriptive_plots import make_descriptive_plots
from .make_map import make_map
Expand Down Expand Up @@ -300,24 +304,18 @@ def update_stacked_bar_plots(
show_all_responses_checked,
):
"""Update the stacked bar plots for all questions based on the selected criteria."""
if show_all_responses_checked:
threshold = None
elif not show_all_responses_checked:
threshold = DEFAULT_QUESTION["outcome"]

figure_lookup_key = (state, is_party_stratify_checked, threshold)

figures = []
for output in ctx.outputs_list:
# Example: {'id': {'question': 'q2', 'type': 'stacked-bar-plot'}, 'property': 'figure'}
question = output["id"]["question"]

if show_all_responses_checked:
threshold = None
elif not show_all_responses_checked:
threshold = DEFAULT_QUESTION["outcome"]

figure = make_stacked_bar(
question=question,
subquestion="all",
state=state,
stratify=is_party_stratify_checked,
threshold=threshold,
)
figures.append(figure)
figures.append(PRERENDERED_BARPLOTS[figure_lookup_key][question])

return figures

Expand Down
25 changes: 22 additions & 3 deletions climate_emotions_map/data_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
"""

import json
import pickle as pkl
from pathlib import Path

import pandas as pd

BASE_PATH = Path(__file__).parents[1]


def load_data_file(file: str) -> pd.DataFrame:
"""Load a TSV data file into a dataframe."""
return pd.read_csv(
Path(__file__).parents[1] / "data" / "survey_results" / file,
BASE_PATH / "data" / "survey_results" / file,
sep="\t",
dtype={"question": str, "sub_question": str, "outcome": str},
)
Expand All @@ -23,7 +26,7 @@ def load_data_file(file: str) -> pd.DataFrame:
def load_data_dictionary(file: str) -> pd.DataFrame:
"""Load a data dictionary TSV into a dataframe."""
return pd.read_csv(
Path(__file__).parents[1] / "data" / "data_dictionaries" / file,
BASE_PATH / "data" / "data_dictionaries" / file,
sep="\t",
# Some data dictionaries have "None" as a meaningful value, so we have to prevent it
# from being interpreted as a NaN by pandas
Expand All @@ -32,6 +35,21 @@ def load_data_dictionary(file: str) -> pd.DataFrame:
)


def load_prerendered_figures(file: str) -> dict:
"""Load a pickle file containing a dictionary of prerendered plotly figures."""
target_file = BASE_PATH / "code/assets" / file
# Because this module always runs the loaders, even when imported by the create_prerendered_figures module
# we need to allow for the file to not exist yet when we want to run the script the first time
if not target_file.exists():
print(
"Prerendered figures not found. Run create_prerendered_figures.py to generate them."
)
return {}

print(f"Loading prerendered figures from {target_file}")
return pkl.load(target_file.open("rb"))


def remove_ignored_rows(df: pd.DataFrame) -> pd.DataFrame:
"""Remove rows from a dataframe that have a value of TRUE in the "ignore" column."""
return df[df["ignore"] == False]
Expand All @@ -40,7 +58,7 @@ def remove_ignored_rows(df: pd.DataFrame) -> pd.DataFrame:
def load_geojson_object(file: str) -> dict:
"""Load a geojson file into a dataframe."""
return json.loads(
(Path(__file__).parents[1] / "code" / "assets" / file).read_text(),
(BASE_PATH / "code" / "assets" / file).read_text(),
)


Expand Down Expand Up @@ -155,3 +173,4 @@ def get_domain_text() -> dict[str, str]:

NATIONAL_SAMPLE_SIZE = SURVEY_DATA["samplesizes_state.tsv"]["n"].sum()
GEOJSON_OBJECTS = load_geojson_objects()
PRERENDERED_BARPLOTS = load_prerendered_figures("prerendered_figures.pkl")
Binary file added code/assets/prerendered_figures.pkl
Binary file not shown.
13 changes: 8 additions & 5 deletions code/create_prerendered_figures.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
make_stacked_bar,
)

unique_questions_list = (
UNIQUE_QUESTIONS = (
DATA_DICTIONARIES["question_dictionary.tsv"]["question"].unique().tolist()
)
unique_states_list = (
UNIQUE_STATES = (
DATA_DICTIONARIES["state_abbreviations.tsv"]["state"].unique().tolist()
)
OUTPUT_FILE = Path(__file__).parents[0] / "assets/prerendered_figures.pkl"


def make_full_set_of_barplots(state=None, stratify=None, threshold=None):
Expand All @@ -27,7 +28,7 @@ def make_full_set_of_barplots(state=None, stratify=None, threshold=None):
"""
return {
question: make_stacked_bar(question, "all", state, stratify, threshold)
for question in unique_questions_list
for question in UNIQUE_QUESTIONS
}


Expand All @@ -40,7 +41,7 @@ def make_all_figures():
"""
figures = {}
# A state of None means we are looking at national level questions
for state in unique_states_list + [None]:
for state in UNIQUE_STATES + [None]:
for stratify in [False, True]:
# For state level figures, we don't stratify by party
if state is not None and stratify:
Expand All @@ -53,5 +54,7 @@ def make_all_figures():

if __name__ == "__main__":
figures = make_all_figures()
with open("prerendered_figures.pkl", "wb") as f:
with OUTPUT_FILE.open("wb") as f:
pkl.dump(figures, f)

print(f"Done prerendering figures to {OUTPUT_FILE}!")

0 comments on commit abd9d8c

Please sign in to comment.