-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor. Split out functions into separate files
- Loading branch information
1 parent
8280b12
commit 52f66bb
Showing
6 changed files
with
159 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from dash import Dash | ||
from typing import List, Dict | ||
import dash_bootstrap_components as dbc | ||
|
||
external_stylesheets = [ | ||
dbc.icons.BOOTSTRAP, | ||
dbc.icons.FONT_AWESOME, | ||
dbc.themes.DARKLY, | ||
"https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.2/dbc.min.css", # https://github.com/AnnMarieW/dash-bootstrap-templates#dbccss--stylesheet | ||
|
||
] | ||
|
||
# Create the Dash app using a factory function | ||
# This lets me use the same app configuration in multiple files without causing circular imports | ||
def create_app(external_stylesheets: List[str], external_scripts: List[Dict[str, str]]) -> Dash: | ||
""" | ||
Create and configure the Dash app. | ||
Args: | ||
external_stylesheets (List[str]): List of external stylesheets to be used. | ||
external_scripts (List[Dict[str, str]]): List of external scripts to be used. Each script is represented as a dictionary. | ||
Returns: | ||
Dash: The configured Dash app. | ||
""" | ||
app = Dash( | ||
__name__, | ||
external_stylesheets=external_stylesheets, | ||
external_scripts=external_scripts, | ||
use_pages=False, | ||
suppress_callback_exceptions=True, | ||
meta_tags = [ | ||
{"name": "viewport", "content": "width=device-width, initial-scale=1"} | ||
], | ||
) | ||
return app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from dash import html | ||
import dash_bootstrap_components as dbc | ||
|
||
title_card = dbc.Card( | ||
[ | ||
html.H3("Xenosaga Enemy Database", className="card-title"), | ||
html.I("Mystic powers, grant me a miracle! ✨", style={"margin-bottom": "10px"}), | ||
html.P( | ||
"This is a mobile-friendly searchable, sortable, and filterable table of all enemies in the Xenosaga series, organized by game.", | ||
style = {"margin-bottom": "0px"} | ||
), | ||
html.P( | ||
"Clicking on anywhere on a row will display the selected enemy's stats in a popup.", | ||
style = {"margin-bottom": "0px"} | ||
), | ||
html.I( # use a GitHub icon for my repo | ||
className="bi bi-github", | ||
style = { | ||
"margin-right": "5px", | ||
"margin-left": "0px" | ||
}, | ||
), | ||
html.A("GitHub", href='https://github.com/perfectly-preserved-pie/xenosaga', target='_blank'), | ||
], | ||
body = True | ||
) | ||
|
||
# Create a modal to display the selected enemy stats | ||
# The modal will be populated by the callback below | ||
modal = dbc.Modal( | ||
[ | ||
dbc.ModalHeader("Selected Enemy Stats"), | ||
dbc.ModalBody(id="modal-content"), | ||
dbc.ModalFooter( | ||
dbc.Button("Close", id="close", className="ml-auto", n_clicks=0) | ||
), | ||
], | ||
id="modal", | ||
is_open=False, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import pandas as pd | ||
|
||
# Read the JSON files into dataframes for each episode | ||
ep1_df = pd.read_json('assets/json/episode1.json', lines=True) | ||
ep2_df = pd.read_json('assets/json/episode2.json', lines=True) | ||
ep3_df = pd.read_json('assets/json/episode3.json', lines=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import pandas as pd | ||
|
||
# Create a function to generate the column definitions based on the dataframe | ||
def generate_column_defs(df): | ||
# Determine if a column is numeric based on a sampling of 100 values from the column | ||
# I did this way because I'm too lazy to properly cast dtypes for the 30+ columns across all 3 episode dataframes | ||
def is_numeric_col(df, column_name): | ||
# If dtype is already numeric, return True | ||
if pd.api.types.is_numeric_dtype(df[column_name].dtype): | ||
return True | ||
# If dtype is object, sample some rows and test if they can be converted to numbers | ||
non_na_values = df[column_name].dropna() # Drop NA values | ||
sample_values = non_na_values.sample(min(100, len(non_na_values))).tolist() | ||
try: | ||
# Try converting the sample values to numbers | ||
[float(x) for x in sample_values] | ||
return True | ||
except ValueError: | ||
# If conversion fails, it's not a numeric column | ||
return False | ||
|
||
# Extracts the starting number from a cell's content, especially if the content represents a range like "100-200" | ||
# This is used to sort the numeric columns properly | ||
def get_value_getter(column_name): | ||
if is_numeric_col(df, column_name): | ||
return {"function": f"return params.data.{column_name} && params.data.{column_name}.split('-')[0] ? Number(params.data.{column_name}.split('-')[0]) : null"} | ||
else: | ||
return None | ||
|
||
# Create the column definitions | ||
# The Name column is special because it's the only column that's pinned to the left | ||
column_defs = [ | ||
{ | ||
"field": "Name", # Set the field to "Name" for the Name column | ||
"minWidth": 150, # Set a minimum width for the Name column | ||
"pinned": "left", # Pin the Name column to the left | ||
"resizable": True, | ||
"sortable": True, | ||
"type": "textColumn", | ||
"filter": "agTextColumnFilter", | ||
"floatingFilter": True, | ||
"floatingFilterComponentParams": {"filterPlaceholder": "Search..."}, | ||
"suppressMenu": True | ||
} | ||
] | ||
# Add other columns except the "Name" or "uuid" column | ||
for i in df.columns: | ||
if i not in ["Name", "uuid"]: | ||
column_def = { | ||
"field": i, | ||
"filter": "agNumberColumnFilter" if is_numeric_col(df, i) else "agTextColumnFilter", | ||
"floatingFilter": True, | ||
"floatingFilterComponentParams": {"suppressFilterButton": False} if is_numeric_col(df, i) else {"filterPlaceholder": "Search..."}, | ||
"minWidth": 120, | ||
"resizable": True, | ||
"sortable": True, | ||
"suppressMenu": True, | ||
"tooltipField": i, # Set the tooltip field to the column name | ||
"type": "numericColumn" if is_numeric_col(df, i) else "textColumn", | ||
"valueFormatter": {"function": "d3.format(',.0f')(params.value)"} if is_numeric_col(df, i) else None, | ||
"valueGetter": get_value_getter(i), | ||
} | ||
# Only add tooltipComponent for string columns | ||
if not is_numeric_col(df, i): | ||
column_def["tooltipComponent"] = "CustomTooltip" | ||
|
||
column_defs.append(column_def) | ||
|
||
return column_defs |