diff --git a/.gitignore b/.gitignore index 12a3c0a..9675115 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,8 @@ cython_debug/ #.idea/ src/.deployment +src/backup +css/backup + +transformation +data diff --git a/CITATION.cff b/CITATION.cff index 2789b8f..42139d7 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -18,6 +18,19 @@ authors: given-names: Jeff affiliation: Weill Cornell Medicine (WCM) orcid: 'https://orcid.org/0009-0006-0366-5050' + - family-names: Bevers + given-names: Isaac + affiliation: Massachusetts Institute of Technology (MIT) + orcid: 'https://orcid.org/0009-0002-0074-0087' + - family-names: Gallois + given-names: Hortense + affiliation: Simon Fraser University + - family-names: Bernier + given-names: Alexander + affiliation: McGill University + - family-names: Bensoussan + given-names: Yaël + affiliation: University of South Florida - family-names: Bensoussan given-names: Yaël affiliation: University of South Florida diff --git a/README.md b/README.md index c00e8f9..a8090e4 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ Docs for the Bridge2AI Voice Project. -[![Github](https://img.shields.io/badge/github-0.4.0-green?style=flat&logo=github)](https://github.com/eipm/bridge2ai-docs) [![Python 3.11.5](https://img.shields.io/badge/python-3.12.0-blue.svg)](https://www.python.org/downloads/release/python-3120/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![DOI](https://zenodo.org/badge/860006845.svg)](https://zenodo.org/doi/10.5281/zenodo.13834653) +[![Github](https://img.shields.io/badge/github-1.0.0-green?style=flat&logo=github)](https://github.com/eipm/bridge2ai-docs) [![Python 3.11.5](https://img.shields.io/badge/python-3.12.0-blue.svg)](https://www.python.org/downloads/release/python-3120/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![DOI](https://zenodo.org/badge/860006845.svg)](https://zenodo.org/doi/10.5281/zenodo.13834653) ## 🤝 License See [LICENSE](./LICENSE) ## 📚 How to Cite -> Sigaras, A., Zisimopoulos, P., Tang, J., Bensoussan, Y., Ghosh, S. S., Rameau, A., Powell, M. E., Belisle-Pipon, J.-C., Ravitsky, V., Johnson, A., Elemento, O., Dorr, D., … Bridge2AI-Voice. (2024). eipm/bridge2ai-docs. Zenodo. [https://zenodo.org/doi/10.5281/zenodo.13834653](https://zenodo.org/doi/10.5281/zenodo.13834653) +> Sigaras, A., Zisimopoulos, P., Tang, J., Bevers, I., Gallois, H., Bernier, A., Bensoussan, Y., Ghosh, S. S., Rameau, A., Powell, M. E., Belisle-Pipon, J.-C., Ravitsky, V., Johnson, A., Elemento, O., Dorr, D., … Bridge2AI-Voice. (2024). eipm/bridge2ai-docs. Zenodo. [https://zenodo.org/doi/10.5281/zenodo.13834653](https://zenodo.org/doi/10.5281/zenodo.13834653) ## Prerequisites diff --git a/css/dashboard.css b/css/dashboard.css new file mode 100644 index 0000000..8ff32b0 --- /dev/null +++ b/css/dashboard.css @@ -0,0 +1,113 @@ +[data-testid="stDecoration"] { + display: none; +} + +[data-testid="stAppDeployButton"]{ + display: none; +} + +[data-testid="stBaseButton-headerNoPadding"] { + display: none; +} + +img[data-testid="stLogo"] { + margin-top: -16px; + margin-left: -20px; + height: 3.5rem; +} + +/* Set the font size for the tab headers */ +button[data-baseweb="tab"] > div[data-testid="stMarkdownContainer"] > p { + font-size: 18px; + white-space: wrap; + word-break: normal; + align-items: flex-start; +} + +.stTabs [data-baseweb="tab-highlight"] { + border-bottom: 4px solid rgb(255, 75, 75); + color: black; +} + +.stTabs [data-baseweb="tab-list"] > button[data-baseweb="tab"] { + display: flex; + align-items: flex-start; +} + +/* Reduce white space on top */ +.block-container { + padding-top: 2rem; +} + +/* Sticky footer */ +footer { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + background: linear-gradient(90deg, #d3d3d3, #f0f0f0); + color: black; + display: flex; + height: 30px; + justify-content: center; + padding: 4px; + z-index: 1000; + font-size: smaller; +} + +@media screen and (max-width: 1278px) { + .stTabs [data-baseweb="tab-list"] { + padding-bottom: 25px; + } +} + +/* set border for plots */ +.js-plotly-plot { + background-color: white; + border: 2px solid #d3d3d3; + border-radius: 5px; + margin-right: -5px; + margin-bottom: -5px; + padding: 0; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +[data-testid="stMetric"] { + border: 2px solid #d3d3d3; + margin-right: -5px; + margin-bottom: -5px; + border-radius: 5px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + background-color: white; +} + +/* set text and metri in columns to center */ +.stMetric > div, [data-testid="stMetricLabel"] { + color: black; + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} + +.js-plotly-plot .plotly .modebar-btn { + font-size: 14px; + padding: 0; + margin: 0; +} + +.js-plotly-plot .plotly .modebar { + top: -1px; + right: 0; + left: auto; + width: 48px; +} + +.legendtext { + transform: translateX(-10px); +} + +/* set weight font for plotly title */ +.gtitle tspan { + font-weight: 500 !important; +} diff --git a/images/B2AI Logo.png b/images/B2AI Logo.png new file mode 100644 index 0000000..f2dc674 Binary files /dev/null and b/images/B2AI Logo.png differ diff --git a/src/dashboard.py b/src/dashboard.py index 898a220..28bb029 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -1,90 +1,12 @@ import streamlit as st -import pandas as pd -import numpy as np -import plotly.express as px - -custom_style = """ - -""" - -def coming_soon_message(tab_name): - # Your Streamlit application code - st.title('Bridge2AI Voice Dashboard') - st.write(f"{tab_name} - Coming soon!") - - # Add an image to the page - image_path = "images/Wave.png" # Replace with your image file name - st.image(image_path, caption='', use_column_width=True) - -def about_page(tab_name): - coming_soon_message(tab_name) - -def healthsheet_page(tab_name): - coming_soon_message(tab_name) - -def study_dashboard_page(tab_name): - coming_soon_message(tab_name) - -def study_metadata_page(tab_name): - coming_soon_message(tab_name) - -def dataset_metadata_page(tab_name): - coming_soon_message(tab_name) - -def dataset_structure_preview_page(tab_name): - coming_soon_message(tab_name) - -def dataset_quality_dashboard_page(tab_name): - coming_soon_message(tab_name) - -def dataset_uses_page(tab_name): - coming_soon_message(tab_name) - -def create_tabs(tabs_func): - tab_names = list(tabs_func.keys()) - tabs = st.tabs(tab_names) - for tab, name in zip(tabs, tab_names): - with tab: - tabs_func[name](name) +from tabs.about import about_page +from tabs.healthsheet import healthsheet_page +from tabs.study_dashboard import study_dashboard_page +from tabs.study_metadata import study_metadata_page +from tabs.dataset_metadata import dataset_metadata_page +from tabs.dataset_structure_preview import dataset_structure_preview_page +from tabs.dataset_quality_dashboard import dataset_quality_dashboard_page +from tabs.dataset_uses import dataset_uses_page def config_page(version): st.set_page_config( @@ -92,23 +14,44 @@ def config_page(version): page_icon="images/B2AI Logo.ico", layout="wide") - st.markdown(custom_style, unsafe_allow_html=True) + # Add the CSS file + with open("css/dashboard.css") as f: + st.markdown(f"", unsafe_allow_html=True) + + st.logo("images/B2AI Logo.png", ) # Add the footer footer = f""" """ st.markdown(footer, unsafe_allow_html=True) +def create_tabs(tabs_func): + tab_names = list(tabs_func.keys()) + tabs = st.tabs(tab_names) + for tab, name in zip(tabs, tab_names): + with tab: + tabs_func[name](name) def main(): - # Define the version variable - version = "0.4.0" + # Current version of the app + version = "1.0.0" # Map tab names to functions + # In this dictionary, the key is the tab name and the value is the function that will be called when the tab is selected + # The function is defined in the respective file + # about_page() is defined in tabs/about.py + # healthsheet_page() is defined in tabs/healthsheet.py + # study_dashboard_page() is defined in tabs/study_dashboard.py + # study_metadata_page() is defined in tabs/study_metadata.py + # dataset_metadata_page() is defined in tabs/dataset_metadata.py + # dataset_structure_preview_page() is defined in tabs/dataset_structure_preview.py + # dataset_quality_dashboard_page() is defined in tabs/dataset_quality_dashboard.py + # dataset_uses_page() is defined in tabs/dataset_uses.py + tab_functions = { "About": about_page, "Healthsheet": healthsheet_page, @@ -126,5 +69,4 @@ def main(): create_tabs(tab_functions) if __name__ == "__main__": - - main() + main() \ No newline at end of file diff --git a/src/tabs/about.py b/src/tabs/about.py new file mode 100644 index 0000000..34f0928 --- /dev/null +++ b/src/tabs/about.py @@ -0,0 +1,5 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +def about_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/dataset_metadata.py b/src/tabs/dataset_metadata.py new file mode 100644 index 0000000..3386039 --- /dev/null +++ b/src/tabs/dataset_metadata.py @@ -0,0 +1,6 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +# Define the content of the Dataset Metadata page +def dataset_metadata_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/dataset_quality_dashboard.py b/src/tabs/dataset_quality_dashboard.py new file mode 100644 index 0000000..f4d7e71 --- /dev/null +++ b/src/tabs/dataset_quality_dashboard.py @@ -0,0 +1,6 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +# Define the content of the Dataset Quality Dashboard page +def dataset_quality_dashboard_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/dataset_structure_preview.py b/src/tabs/dataset_structure_preview.py new file mode 100644 index 0000000..fe37358 --- /dev/null +++ b/src/tabs/dataset_structure_preview.py @@ -0,0 +1,6 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +# Define the content of the Dataset Structure Preview page +def dataset_structure_preview_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/dataset_uses.py b/src/tabs/dataset_uses.py new file mode 100644 index 0000000..2a37708 --- /dev/null +++ b/src/tabs/dataset_uses.py @@ -0,0 +1,6 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +# Define the content of the Dataset Uses page +def dataset_uses_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/healthsheet.py b/src/tabs/healthsheet.py new file mode 100644 index 0000000..04850e8 --- /dev/null +++ b/src/tabs/healthsheet.py @@ -0,0 +1,6 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +# Define the content of the Health Sheet page +def healthsheet_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/study_dashboard.py b/src/tabs/study_dashboard.py new file mode 100644 index 0000000..771cb4c --- /dev/null +++ b/src/tabs/study_dashboard.py @@ -0,0 +1,316 @@ +import streamlit as st +import pandas as pd +import plotly.express as px +import json + +def load_data(): + # Read the JSON object from the file + with open('data/dashboard_data.json', 'r') as json_file: + data = json.load(json_file) + return data + +def get_data(json_data, tag, name_mapping=None): + data = json_data.get(tag) + names = list(data.keys()) + values = list(data.values()) + if name_mapping: + new_names = [name_mapping.get(name, name) for name in names] + return new_names, values + return names, values + +def create_pie_chart(names, values, title, props={'height': 400, 'color_discrete_sequence': px.colors.qualitative.D3, 'y': -0.3, 'entry_width': 0.5, 'font_size': 11}): + fig = px.pie( + names=names, values=values, + category_orders={'names': names}, + color_discrete_sequence=props['color_discrete_sequence'], + hole=0.5) # donut chart + + title_setting = { + 'text': title, + 'font': { + 'size': 16, + 'color': 'black', + 'family': 'Source Sans Pro, sans-serif' + }, + 'x': 0.005, + 'y': 0.99, + 'xanchor': 'left', + 'yanchor': 'top', + } + + fig.update_layout( + autosize=True, + showlegend=True, + paper_bgcolor='white', + plot_bgcolor='white', + margin=dict(l=0, r=0, t=30, b=0), + height=props['height'], + title=title_setting, + legend=dict( + x=0, + y=props['y'], + xanchor='left', + yanchor='bottom', + orientation='h', + traceorder='normal', + font=dict(size=props['font_size'], color="black",family='Source Sans Pro, sans-serif', lineposition='none'), + itemwidth=30, + itemsizing='trace', + valign='top', + entrywidthmode='fraction', + entrywidth=props['entry_width'], + indentation= -5, + tracegroupgap=0 + ) + ) + + fig.update_traces( + marker=dict(line=dict(color='black', width=0.5)), + textposition='inside', + textfont=dict(family='Source Sans Pro, sans-serif'), + texttemplate="%{value}
(%{percent:.2%})", + hovertemplate="%{label}
%{percent:.2%}(%{value})", + domain=dict(x=[0, 1], y=[0, 1]) + ) + + return fig + +def get_asis_chart_property(text, font_size=11): + return { + 'title': { + 'text': text, + 'font': { + 'size': font_size, + 'color': 'black', + 'family': 'Source Sans Pro, sans-serif' + } + }, + 'tickfont': { + 'size': font_size, + 'color': 'black', + 'family': 'Source Sans Pro, sans-serif' + }, + 'showgrid': True, + 'gridcolor': 'lightgray', + } + +def create_bar_chart(names, values, title, props={'height': 400, 'individual_color': False, 'color_discrete_sequence': px.colors.qualitative.D3, 'orientation':'v', 'x': '', 'y': '', 'font_size': 11}): + fig = px.bar( + x=values if props['orientation'] == 'h' else names, + y=names if props['orientation'] == 'h' else values, + orientation=f"{props['orientation']}", + color=names if props['individual_color'] else None, + color_discrete_sequence=props['color_discrete_sequence']) + + fig.update_layout( + xaxis=get_asis_chart_property(props['x'], props['font_size']), + yaxis=get_asis_chart_property(props['y'], props['font_size']), + autosize=True, + showlegend=False, + paper_bgcolor='white', + plot_bgcolor='white', + height=props['height'], + margin=dict(l=5, r=5, t=30, b=5), + title={ + 'text': title, + 'font': { + 'size': 16, + 'color': 'black', + 'family': 'Source Sans Pro, sans-serif' + }, + 'x': 0.001, + 'y': 0.99, + 'xanchor': 'left', + 'yanchor': 'top', + } + ) + fig.update_traces( + marker=dict(line=dict(color='black', width=0.5)), + textposition='auto', + textfont=dict(family='Source Sans Pro, sans-serif'), + textangle=0, + texttemplate="%{x}", + hovertemplate='%{y}
%{x}', + ) + + return fig + +def getPlotlyConfig(): + return { + 'displayModeBar': True, + 'displaylogo': False, + 'modeBarButtonsToRemove': [ + 'zoom2d', 'pan2d', 'select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', + 'autoScale2d', 'resetScale2d' + ] + } + +def create_plots(data, plots, cols_per_row=4): + num_plots = len(plots) + rows = (num_plots + cols_per_row - 1) // cols_per_row # Calculate number of rows needed + + for row in range(rows): + cols = st.columns(cols_per_row, gap="small", vertical_alignment="top") + for col_index in range(cols_per_row): + plot_index = row * cols_per_row + col_index + if plot_index < num_plots: + plot = plots[plot_index] + key, title, chart_type, plot_props = plot[:4] + # Optional name mapping for charts + name_mapping = plot[4] if len(plot) == 5 else None + + if key and title and chart_type: + labels, values = get_data(data, key, name_mapping) + + if num_plots <= rows*cols_per_row+col_index: + if chart_type is None: + cols[col_index].empty() + else: + if chart_type == 'pie': + fig = create_pie_chart(labels, values, title, plot_props) + elif chart_type == 'horizontal_bar': + fig = create_bar_chart(labels, values, title, plot_props) + + elif chart_type == 'vertical_bar': + fig = create_bar_chart(labels, values, title, plot_props) + cols[col_index].plotly_chart(fig, use_container_width=True, config=getPlotlyConfig()) + else: + cols[col_index].empty() + +def overview_section(data): + number_of_participants = data.get('number_of_participants') + number_of_recordings = data.get('number_of_recordings') + total_hours_of_recordings = data.get('total_hours_of_recordings') + total_questionnaire_collected = pd.json_normalize(data.get('questionnaire_collected')).values.sum() + total_acoustic_task_collected = pd.json_normalize(data.get('acoustic_task_collected')).values.sum() + + if isinstance(total_hours_of_recordings, float): + total_hours_of_recordings = round(total_hours_of_recordings, 2) + + cards = [ + ("Number of Participants", number_of_participants), + ("Number of Recordings", number_of_recordings), + ("Total of Questionnaires", total_questionnaire_collected), + ("Total of Acoustic Tasks", total_acoustic_task_collected), + ("Total Hours of Recordings", total_hours_of_recordings) + ] + + # Create a 5-column layout for the metrics + columns = st.columns([1, 1, 1, 1, 1]) + for i, col in enumerate(columns): + name, value = cards[i] + if name is not None and value is not None: + col.metric(name, value) + else: + col.empty() + +def data_collection_section(data, collected_data): + columns = st.columns([2,2,1]) + # Define columns for questionnaires and acoustic tasks + column_configs = { + 'questionnaire_collected': { + "name": st.column_config.TextColumn( + "Questionnaire", + width="large" + ), + "value": st.column_config.TextColumn( + "Count", + width="small" + ) + }, + 'acoustic_task_collected': { + "name": st.column_config.TextColumn( + "Acoustic Task", + width="large" + ), + "value": st.column_config.TextColumn( + "Count", + width="small" + ) + } + } + + for index, (key, title) in enumerate(collected_data): + names, values = get_data(data, key) + df = pd.DataFrame({"name": names, "value": values}) + with columns[index]: + st.dataframe(df,column_config = column_configs[key], hide_index=True) + +def study_dashboard_page(tab_name): + data = load_data() + if not data: + st.write("No data available") + return + + # Pre-defined colors for plots + colors = [ + '#D21AE8', + '#63A9FF', + '#FF7820', + '#FF4121', + '#18ED84', + '#FCF500', + '#7E04E9', + '#F109AE', + '#0FB6B5', + '#1D8AD7' + ] + + # Demographic plots + # params: key, title, chart_type, props, name_mapping + # key: key in the JSON object + # title: title of the chart + # chart_type: type of the chart (pie, horizontal_bar, vertical_bar, table) + # props: plot properties + # name_mapping: mapping of names to be displayed in the chart if needed + demographic_plots = [ + ('control', 'Control Group vs Diagnostic Group', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.26, 'entry_width': 0.33, 'font_size': 11}, {'Yes': 'Control Group', 'No': 'Diagnostic Group'}), + ('gender_identity', 'Gender Identity', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.26, 'entry_width': 0.5, 'font_size': 11}, {'Female gender identity': 'Female', 'Male gender identity': 'Male', 'Non-binary or genderqueer gender identity': 'Non-binary/genderqueer'}), + ('sexual_orientation', 'Sexual Orientation', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.26, 'entry_width': 0.33, 'font_size': 11}), + ('race', 'Race', 'horizontal_bar', {'height': 450, 'individual_color': True, 'color_discrete_sequence': colors, 'orientation':'h', 'x': 'Count', 'y': 'Race Categories', 'font_size': 11}, {'American Indian or Alaska Native': 'American Indian/Alaska Native', 'Native Hawaiian or other Pacific Islander': 'Native Hawaiian/other Pacific Islander', 'Canadian Indigenous or Aboriginal': 'Canadian Indigenous/Aboriginal'}), + ('ethnicity', 'Ethnicity', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.26, 'entry_width': 0.33, 'font_size': 11}), + ('primary_language', 'Primary Language', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.26, 'entry_width': .15, 'font_size': 11}), + ('age_groups','Age', 'horizontal_bar', {'height': 450, 'individual_color': False, 'color_discrete_sequence': colors, 'orientation':'h', 'x': 'Number of Participants', 'y': 'Age Groups', 'font_size': 11}, {'90 and above': '90 and
above'}) + ] + + # Disorder plots + # params: key, title, chart_type, props, name_mapping + # key: key in the JSON object + # title: title of the chart + # chart_type: type of the chart (pie, horizontal_bar, vertical_bar, table) + # props: plot properties + # name_mapping: mapping of names to be displayed in the chart if needed + disorder_plots = [ + ('disorder_types', 'Diagnostic Group', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.3, 'entry_width': 0.45, 'font_size': 11}, {'Neurological and Neurodegenerative Disorders': 'Neurological and Neurodegenerative
Disorders'}), + ('voice_disorders_category', 'Voice Disorder', 'horizontal_bar', {'height': 450, 'individual_color': False, 'color_discrete_sequence': colors, 'orientation':'h', 'x': 'Count', 'y': 'Voice Disorder Categories', 'font_size': 11}, {'Lesions of the vocal cord (nodule, polyp, cyst)': 'Lesions of the vocal cord','Spasmodic Dysphonia / Laryngeal Tremor': 'Spasmodic Dysphonia/Laryngeal Tremor'}), + ('neurological_and_neurodegenerative_disorders_category', 'Neurological and Neurodegenerative Disorder', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.3, 'entry_width': 1, 'font_size': 11}), + ('mood_and_psychiatric_disorders_category', 'Mood and Psychiatric Disorder', 'horizontal_bar', {'height': 450, 'individual_color': False, 'color_discrete_sequence': colors, 'orientation':'h', 'x': 'Count', 'y': 'Mood and Psychiatric Disorder Categories', 'font_size': 11}, {'Attention-Deficit / Hyperactivity Disorder (ADHD)': 'Attention-Deficit/Hyperactivity Disorder', 'Insomnia / Sleep Disorder': 'Insomnia/Sleep Disorder'}), + ('respiratory_disorders_category', 'Respiratory Disorder', 'pie', {'height': 450, 'color_discrete_sequence': colors, 'y': -0.3, 'entry_width': 0.5, 'font_size': 11}, {'Airway Stenosis (for example: bilateral vocal fold paralysis; laryngeal stenosis)': 'Airway Stenosis'}), + ] + + # Data collection plots + collected_data_plots = [ + ('questionnaire_collected', 'Questionnaire Collection', 'horizontal_bar', {'height': 600, 'individual_color': False, 'color_discrete_sequence': colors, 'orientation':'h', 'x': 'Count', 'y': 'Questionnaire Categories', 'font_size': 11}), + ('acoustic_task_collected', 'Acoustic Task Collection', 'horizontal_bar', {'height': 600, 'individual_color': False, 'color_discrete_sequence': colors, 'orientation':'h', 'x': 'Count', 'y': 'Acoustic Task Categories', 'font_size': 11}), + ] + + # Overview Section + st.subheader("Overview") + overview_section(data) + + # Disorders Section + st.subheader("Diagnostic Breakdown") + # Create the disorder plots + # params: data, plots, cols_per_row + create_plots(data, disorder_plots, 3) + + # Demographic Section + st.subheader("Demographic Breakdown") + # Create the demographic plots + # params: data, plots, cols_per_row + create_plots(data, demographic_plots, 3) + + # Data Collection Section + #params: data, plots, cols_per_row + st.subheader("Data Collection") + create_plots(data, collected_data_plots, 2) \ No newline at end of file diff --git a/src/tabs/study_metadata.py b/src/tabs/study_metadata.py new file mode 100644 index 0000000..e5dab46 --- /dev/null +++ b/src/tabs/study_metadata.py @@ -0,0 +1,6 @@ +import streamlit as st +from tabs.utils import coming_soon_message + +# Define the content of the Study Metadata page +def study_metadata_page(tab_name): + coming_soon_message(tab_name) \ No newline at end of file diff --git a/src/tabs/utils.py b/src/tabs/utils.py new file mode 100644 index 0000000..ffde6aa --- /dev/null +++ b/src/tabs/utils.py @@ -0,0 +1,9 @@ +import streamlit as st + +# All tab pages are defined below +def coming_soon_message(tab_name): + st.title('Bridge2AI Voice Dashboard') + st.write(f"{tab_name} - Coming soon!") + + image_path = "images/Wave.png" + st.image(image_path, caption='', use_column_width=True)