-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create skeleton of the dashboard (#22)
* add app main script * add css custom styles file * add requirements * add light/dark switch and set the same colour as mkdocs * add upload functionality using dash-uploader * add mockup data * add disabled tabs functionality * add tests Co-authored-by: Cunliang Geng <[email protected]>
- Loading branch information
1 parent
5f63f1b
commit c773794
Showing
11 changed files
with
255 additions
and
9 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
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
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,191 @@ | ||
import os | ||
import pickle | ||
import tempfile | ||
import uuid | ||
import dash_bootstrap_components as dbc | ||
import dash_uploader as du | ||
from dash import Dash | ||
from dash import Input | ||
from dash import Output | ||
from dash import clientside_callback | ||
from dash import dcc | ||
from dash import html | ||
|
||
|
||
dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css" | ||
app = Dash(__name__, external_stylesheets=[dbc.themes.UNITED, dbc_css, dbc.icons.FONT_AWESOME]) | ||
|
||
# ------------------ Nav Bar ------------------ # | ||
color_mode_switch = html.Span( | ||
[ | ||
dbc.Label(className="fa fa-moon", html_for="color-mode-switch"), | ||
dbc.Switch( | ||
id="color-mode-switch", | ||
value=False, | ||
className="d-inline-block ms-1", | ||
persistence=True, | ||
), | ||
dbc.Label(className="fa fa-sun", html_for="color-mode-switch"), | ||
], | ||
className="p-2", | ||
) | ||
|
||
# Define the navigation bar | ||
navbar = dbc.Row( | ||
dbc.Col( | ||
dbc.NavbarSimple( | ||
children=[ | ||
dbc.NavItem(dbc.NavLink("Doc", href="https://nplinker.github.io/nplinker/latest/")), | ||
dbc.NavItem( | ||
dbc.NavLink("About", href="https://github.com/NPLinker/nplinker-webapp"), | ||
), | ||
dbc.NavItem( | ||
color_mode_switch, | ||
className="mt-1 p-1", | ||
), | ||
], | ||
brand="NPLinker Webapp", | ||
color="primary", | ||
className="p-3 mb-2", | ||
dark=True, | ||
), | ||
), | ||
) | ||
|
||
# ------------------ Uploader ------------------ # | ||
# Configure the upload folder | ||
TEMP_DIR = tempfile.mkdtemp() | ||
du.configure_upload(app, TEMP_DIR) | ||
|
||
uploader = html.Div( | ||
[ | ||
dbc.Row( | ||
dbc.Col( | ||
du.Upload( | ||
id="dash-uploader", | ||
text="Import Data", | ||
text_completed="Uploaded: ", | ||
filetypes=["pkl", "pickle"], | ||
upload_id=uuid.uuid1(), # Unique session id | ||
cancel_button=True, | ||
max_files=1, | ||
), | ||
) | ||
), | ||
dbc.Row( | ||
dbc.Col( | ||
html.Div(children="No file uploaded", id="dash-uploader-output", className="p-4"), | ||
className="d-flex justify-content-center", | ||
) | ||
), | ||
dcc.Store(id="file-store"), # Store to keep the file contents | ||
], | ||
className="p-5 ml-5 mr-5", | ||
) | ||
|
||
# ------------------ Tabs ------------------ # | ||
# gm tab content | ||
gm_content = dbc.Row( | ||
dbc.Col( | ||
dbc.Card( | ||
dbc.CardBody([html.Div(id="file-content-gm")]), | ||
) | ||
) | ||
) | ||
# mg tab content | ||
mg_content = dbc.Row( | ||
dbc.Col( | ||
dbc.Card( | ||
dbc.CardBody([html.Div(id="file-content-mg")]), | ||
) | ||
), | ||
) | ||
# tabs | ||
tabs = dbc.Row( | ||
dbc.Col( | ||
dbc.Tabs( | ||
[ | ||
dbc.Tab( | ||
gm_content, | ||
label="Genomics -> Metabolomics", | ||
activeTabClassName="fw-bold", | ||
disabled=True, | ||
id="gm-tab", | ||
className="disabled-tab", | ||
), | ||
dbc.Tab( | ||
mg_content, | ||
label="Metabolomics -> Genomics", | ||
activeTabClassName="fw-bold", | ||
disabled=True, | ||
id="mg-tab", | ||
className="disabled-tab", | ||
), | ||
], | ||
), | ||
), | ||
className="p-5", | ||
) | ||
|
||
# ------------------ App Layout ------------------ # | ||
app.layout = dbc.Container([navbar, uploader, tabs], fluid=True, className="p-0") | ||
|
||
# ------------------ Callbacks------------------ # | ||
clientside_callback( | ||
""" | ||
(switchOn) => { | ||
document.documentElement.setAttribute('data-bs-theme', switchOn ? 'light' : 'dark'); | ||
return window.dash_clientside.no_update | ||
} | ||
""", | ||
Output("color-mode-switch", "id"), | ||
Input("color-mode-switch", "value"), | ||
) | ||
|
||
|
||
@du.callback( | ||
id="dash-uploader", | ||
output=[Output("dash-uploader-output", "children"), Output("file-store", "data")], | ||
) | ||
def upload_data(status: du.UploadStatus): # noqa: D103 | ||
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), | ||
) | ||
return "No file uploaded", None | ||
|
||
|
||
@app.callback( | ||
[Output("gm-tab", "disabled"), Output("mg-tab", "disabled")], | ||
[Input("file-store", "data")], | ||
prevent_initial_call=True, | ||
) | ||
def disable_tabs(file_name): # noqa: D103 | ||
if file_name is None: | ||
# Disable the tabs | ||
return True, True | ||
# Enable the tabs | ||
return False, False | ||
|
||
|
||
# Define another callback to access the stored file path and read the file | ||
@app.callback( | ||
[Output("file-content-gm", "children"), Output("file-content-mg", "children")], | ||
[Input("file-store", "data")], | ||
) | ||
def display_file_contents(file_path): # noqa: D103 | ||
if file_path is not None: | ||
with open(file_path, "rb") as f: | ||
data = pickle.load(f) | ||
# Process and display the data as needed | ||
content = f"File contents: {data[0][:2]}" | ||
return content, content # Display same content in both tabs | ||
return "No data available", "No data available" | ||
|
||
|
||
if __name__ == "__main__": | ||
app.run_server(debug=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,11 @@ | ||
:root { | ||
--bs-primary: #FF6E42; /* Replace with your desired primary color */ | ||
} | ||
|
||
.navbar { | ||
background-color: var(--bs-primary) !important; | ||
} | ||
|
||
.nav-tabs .nav-item .nav-link.disabled { | ||
color: #888888 !important; /* Set your desired color here */ | ||
} |
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 |
---|---|---|
|
@@ -4,4 +4,5 @@ isort | |
ruff | ||
mypy | ||
yapf | ||
pytest | ||
pytest | ||
dash[testing] |
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 |
---|---|---|
@@ -1,3 +1,7 @@ | ||
nplinker==2.0.0a2 | ||
nplinker==2.0.0a3 | ||
dash | ||
pandas | ||
dash-bootstrap-components | ||
dash_bootstrap_templates | ||
numpy | ||
dash-uploader==0.7.0a1 | ||
packaging==21.3.0 |
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,4 @@ | ||
from pathlib import Path | ||
|
||
|
||
DATA_DIR = Path(__file__).resolve().parent / "data" |
Binary file not shown.
This file was deleted.
Oops, something went wrong.
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,31 @@ | ||
from dash_uploader import UploadStatus | ||
from app.app import disable_tabs | ||
from app.app import upload_data | ||
from . import DATA_DIR | ||
|
||
|
||
MOCK_FILE_PATH = DATA_DIR / "mock_obj_data.pkl" | ||
|
||
|
||
def test_upload_data(): | ||
# Create an UploadStatus object | ||
status = UploadStatus( | ||
uploaded_files=[MOCK_FILE_PATH], n_total=1, uploaded_size_mb=5.39, total_size_mb=5.39 | ||
) | ||
upload_string, path_string = upload_data(status) | ||
|
||
# Check the result | ||
assert upload_string == f"Successfully uploaded: {MOCK_FILE_PATH.name} [5.39 MB]" | ||
assert path_string == str(MOCK_FILE_PATH) | ||
|
||
|
||
def test_disable_tabs(): | ||
# Test with None as input | ||
result = disable_tabs(None) | ||
assert result[0] is True # GM tab should be disabled | ||
assert result[1] is True # MG tab should be disabled | ||
|
||
# Test with a string as input | ||
result = disable_tabs(MOCK_FILE_PATH) | ||
assert result[0] is False # GM tab should be enabled | ||
assert result[1] is False # MG tab should be enabled |
This file was deleted.
Oops, something went wrong.