Skip to content

Commit

Permalink
merge with 18_genomics_filter_gcroci2
Browse files Browse the repository at this point in the history
  • Loading branch information
gcroci2 committed Aug 9, 2024
2 parents 18d424a + 18ebd60 commit 94d674d
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 159 deletions.
33 changes: 17 additions & 16 deletions app/assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@
color: #888888 !important; /* Set your desired color here */
}

.filter-button {
background-color: #FF6E42 !important; /* Change to your primary color */
border-color: #FF6E42 !important; /* Ensure the border color matches */
color: white !important; /* Ensure text color is readable */
.btn-primary {
/* Use theme color or styles explicitly */
background-color: #FF6E42 !important; /* Primary color in the UNITED theme */
border-color: #f19e85 !important;
}

.filter-button:focus,
.filter-button:active {
outline: none !important; /* Remove the default outline */
box-shadow: 0 0 0 0.2rem rgba(255, 110, 66, 0.5) !important; /* Add a custom focus shadow */
.btn-primary:hover {
background-color: #e0400f !important; /* Hover color */
border-color: #f19e85 !important;
}

.custom-dropdown-toggle {
background-color: #FF6E42 !important; /* Primary color */
border-color: #FF6E42 !important; /* Match border color */
color: white !important; /* Ensure text color is readable */
.btn-primary:active {
background-color: #e0400f !important;
border-color: #f19e85 !important;
}

.custom-dropdown-toggle:focus,
.custom-dropdown-toggle:active {
outline: none !important; /* Remove default outline */
box-shadow: 0 0 0 0.2rem rgba(255, 110, 66, 0.5) !important; /* Custom focus shadow */
.btn-primary:focus {
box-shadow: 0 0 0 0.2rem #e0400f !important; /* Focus outline color */
border-color: #f19e85 !important; /* Focus border color */
}

.custom-textinput .mantine-TextInput-input:focus {
border-color: #FF6E42 !important; /* Customize border color */
}
220 changes: 176 additions & 44 deletions app/callbacks.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import os
import pickle
import tempfile
import uuid
from typing import Any
from typing import Optional
import dash
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
import dash_uploader as du
import plotly.graph_objects as go
from dash import ALL
from dash import MATCH
from dash import Dash
from dash import Input
from dash import Output
from dash import State
from dash import callback_context as ctx
from dash import clientside_callback
from dash import dcc
from dash import html
from .config import GM_DROPDOWN_BGC_CLASS_OPTIONS
from .config import GM_DROPDOWN_BGC_CLASS_PLACEHOLDER
from .config import GM_DROPDOWN_MENU_OPTIONS
from .config import GM_TEXT_INPUT_IDS_PLACEHOLDER


dash._dash_renderer._set_react_version("18.2.0")


dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css"
Expand All @@ -34,37 +50,55 @@
id="dash-uploader",
output=[Output("dash-uploader-output", "children"), Output("file-store", "data")],
)
def upload_data(status: du.UploadStatus): # noqa: D103
def upload_data(status: du.UploadStatus) -> tuple[str, Optional[str]]:
"""Handle file upload and validate pickle files.
Args:
status: The upload status object.
Returns:
A tuple containing a message string and the file path (if successful).
"""
if status.is_completed:
latest_file = status.latest_file
with open(status.latest_file, "rb") as f:
pickle.load(f)
return (
f"Successfully uploaded: {os.path.basename(latest_file)} [{round(status.uploaded_size_mb, 2)} MB]",
str(latest_file),
)
try:
with open(status.latest_file, "rb") as f:
pickle.load(f)
return (
f"Successfully uploaded: {os.path.basename(latest_file)} [{round(status.uploaded_size_mb, 2)} MB]",
str(latest_file),
)
except (pickle.UnpicklingError, EOFError, AttributeError):
return f"Error: {os.path.basename(latest_file)} is not a valid pickle file.", None
except Exception as e:
# Handle any other unexpected errors
return f"Error uploading file: {str(e)}", None
return "No file uploaded", None


@app.callback(
[
Output("gm-tab", "disabled"),
Output("gm-filter-button", "disabled"),
Output("gcf-ids-dropdown-menu", "disabled"),
Output("gcf-ids-dropdown-input", "disabled"),
Output("gcf-bigscape-dropdown-menu", "disabled"),
Output("gcf-bigscape-dropdown-input", "disabled"),
Output("gm-accordion-control", "disabled"),
Output("mg-tab", "disabled"),
],
[Input("file-store", "data")],
prevent_initial_call=True,
)
def disable_tabs(file_name): # noqa: D103
def disable_tabs(file_name: Optional[str]) -> tuple[bool, bool, bool]:
"""Enable or disable tabs based on whether a file has been uploaded.
Args:
file_name: The name of the uploaded file.
Returns:
A tuple of boolean values indicating whether each tab should be disabled.
"""
if file_name is None:
# Disable the tabs
return True, True, True, True, True, True, True
return True, True, True
# Enable the tabs
return False, False, False, False, False, False, False
return False, False, False


# Define another callback to access the stored file path and read the file
Expand All @@ -74,7 +108,15 @@ def disable_tabs(file_name): # noqa: D103
Output("file-content-mg", "children"),
[Input("file-store", "data")],
)
def gm_plot(file_path): # noqa: D103
def display_file_contents(file_path: Optional[str]) -> str:
"""Read and display the contents of the uploaded file.
Args:
file_path: The path to the uploaded file.
Returns:
A string representation of the file contents.
"""
if file_path is not None:
with open(file_path, "rb") as f:
data = pickle.load(f)
Expand Down Expand Up @@ -124,39 +166,129 @@ def gm_plot(file_path): # noqa: D103


@app.callback(
Output("gm-filter-collapse", "is_open"),
[Input("gm-filter-button", "n_clicks")],
[State("gm-filter-collapse", "is_open")],
Output("blocks-id", "data"),
Input({"type": "gm-add-button", "index": ALL}, "n_clicks"),
State("blocks-id", "data"),
)
def toggle_gm_filter_collapse(n, is_open): # noqa: D103
if n:
return not is_open
return is_open
def add_block(n_clicks: list[int], blocks_id: list[str]) -> list[str]:
"""Add a new block to the layout when the add button is clicked.
Args:
n_clicks: List of number of clicks for each add button.
blocks_id: Current list of block IDs.
Returns:
Updated list of block IDs.
"""
if not any(n_clicks):
raise dash.exceptions.PreventUpdate
# Create a unique ID for the new block
new_block_id = str(uuid.uuid4())
blocks_id.append(new_block_id)
return blocks_id


@app.callback(
Output("gcf-ids-dropdown-input", "value"),
Output("gcf-bigscape-dropdown-input", "value"),
[
Input("gcf-ids-dropdown-input", "value"),
Input("gcf-ids-dropdown-clear", "n_clicks"),
Input("gcf-bigscape-dropdown-input", "value"),
Input("gcf-bigscape-dropdown-clear", "n_clicks"),
],
Output("blocks-container", "children"),
Input("blocks-id", "data"),
State("blocks-container", "children"),
)
def display_blocks(
blocks_id: list[str], existing_blocks: list[dict[str, Any]]
) -> list[dict[str, Any]]:
"""Update the display of blocks based on the current block IDs.
Args:
blocks_id: List of block IDs.
existing_blocks: Current list of block components.
Returns:
Updated list of block components.
"""
new_block_id = blocks_id[-1]

new_block = dmc.Grid(
id={"type": "gm-block", "index": new_block_id},
children=[
dmc.GridCol(
dbc.Button(
[html.I(className="fas fa-plus")],
id={"type": "gm-add-button", "index": new_block_id},
className="btn-primary",
),
span=1,
),
dmc.GridCol(
dcc.Dropdown(
options=GM_DROPDOWN_MENU_OPTIONS,
value="GCF_ID",
id={"type": "gm-dropdown-menu", "index": new_block_id},
clearable=False,
),
span=6,
),
dmc.GridCol(
[
dmc.TextInput(
id={"type": "gm-dropdown-ids-text-input", "index": new_block_id},
placeholder=GM_TEXT_INPUT_IDS_PLACEHOLDER,
className="custom-textinput",
),
dcc.Dropdown(
id={"type": "gm-dropdown-bgc-class-dropdown", "index": new_block_id},
options=GM_DROPDOWN_BGC_CLASS_OPTIONS,
multi=True,
style={"display": "none"},
),
],
span=5,
),
],
gutter="md",
)

# Hide the add button on the previous last block
existing_blocks[-1]["props"]["children"][0]["props"]["children"]["props"]["style"] = {
"display": "none"
}

return existing_blocks + [new_block]


@app.callback(
Output({"type": "gm-dropdown-ids-text-input", "index": MATCH}, "style"),
Output({"type": "gm-dropdown-bgc-class-dropdown", "index": MATCH}, "style"),
Output({"type": "gm-dropdown-ids-text-input", "index": MATCH}, "placeholder"),
Output({"type": "gm-dropdown-bgc-class-dropdown", "index": MATCH}, "placeholder"),
Output({"type": "gm-dropdown-ids-text-input", "index": MATCH}, "value"),
Output({"type": "gm-dropdown-bgc-class-dropdown", "index": MATCH}, "value"),
Input({"type": "gm-dropdown-menu", "index": MATCH}, "value"),
)
def gm_filter(gcf_ids, gcf_ids_clear, gcf_bigscape, gcf_bigscape_clear): # noqa: D103
ctx = dash.callback_context
def update_placeholder(
selected_value: str,
) -> tuple[dict[str, str], dict[str, str], str, str, str, list[Any]]:
"""Update the visibility and placeholders of input fields based on the selected dropdown value.
Args:
selected_value: The value selected in the dropdown menu.
Returns:
A tuple containing style, placeholder, and value updates for the input fields.
"""
if not ctx.triggered:
return "", ""
# Callback was not triggered by user interaction, don't change anything
raise dash.exceptions.PreventUpdate
if selected_value == "GCF_ID":
return {"display": "block"}, {"display": "none"}, GM_TEXT_INPUT_IDS_PLACEHOLDER, "", "", []
elif selected_value == "BGC_CLASS":
return (
{"display": "none"},
{"display": "block"},
"",
GM_DROPDOWN_BGC_CLASS_PLACEHOLDER,
"",
[],
)
else:
button_id = ctx.triggered[0]["prop_id"].split(".")[0]

if button_id == "gcf-ids-dropdown-clear":
return "", gcf_bigscape
elif button_id == "gcf-ids-dropdown-input":
return gcf_ids, gcf_bigscape
elif button_id == "gcf-bigscape-dropdown-clear":
return gcf_ids, ""
elif button_id == "gcf-bigscape-dropdown-input":
return gcf_ids, gcf_bigscape
# This case should never occur due to the Literal type, but it satisfies mypy
return {"display": "none"}, {"display": "none"}, "", "", "", []
19 changes: 19 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
GM_DROPDOWN_MENU_OPTIONS = [
{"label": "GCF ID", "value": "GCF_ID"},
{"label": "BGC Class", "value": "BGC_CLASS"},
]

GM_TEXT_INPUT_IDS_PLACEHOLDER = "1, 2, 3, ..."

GM_DROPDOWN_BGC_CLASS_OPTIONS = [
{"label": "Alkaloid", "value": "ALKALOID"},
{"label": "NRP", "value": "NRP"},
{"label": "Polyketide", "value": "POLYKETIDE"},
{"label": "RiPP", "value": "RIPP"},
{"label": "Saccharide", "value": "SAACCHARIDE"},
{"label": "Terpene", "value": "TERPENE"},
{"label": "Other", "value": "OTHER"},
{"label": "Unknown", "value": "UNKNOWN"},
]

GM_DROPDOWN_BGC_CLASS_PLACEHOLDER = "Select one or more BGC classes"
Loading

0 comments on commit 94d674d

Please sign in to comment.