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)