diff --git a/src/web/layout/__init__.py b/src/web/layout/__init__.py new file mode 100644 index 0000000..d2bc031 --- /dev/null +++ b/src/web/layout/__init__.py @@ -0,0 +1 @@ +from .layout import app_layout \ No newline at end of file diff --git a/src/web/layout/alerts.py b/src/web/layout/alerts.py new file mode 100644 index 0000000..07845b5 --- /dev/null +++ b/src/web/layout/alerts.py @@ -0,0 +1,14 @@ +from dash import html, dcc +import dash_bootstrap_components as dbc + + +alerts_element = html.Div([ + dbc.Alert( + children="", + id="alert_element", + dismissable=True, + fade=False, + is_open=False, + ), + dcc.Store(id="alert_message") +]) diff --git a/src/web/layout/body.py b/src/web/layout/body.py index 1e5e1c9..c88bb59 100644 --- a/src/web/layout/body.py +++ b/src/web/layout/body.py @@ -2,142 +2,19 @@ import dash_mantine_components as dmc from dash import dcc, html from typing import List -from src.model_experiential import ExperientialModelOutput -from src.model_interpersonal import ( - ContactModelOutput, - AngleModelOutput, - PointOfViewModelOutput, - DistanceModelOutput, - ModalityLightingModelOutput, - ModalityColorModelOutput, - ModalityDepthModelOutput -) -from src.model_textual import ( - InformationValueModelOutput, - FramingModelOutput, - SalienceModelOutput -) +from .image import image_element +from .inputs import inputs_element -def generate_option_labels(model) -> List[str]: - """Generate presentable list of attributes from an OutputModel.""" - labels = [ - label.replace('_', ' ').title() - for label in model.list_fields() - ] - return labels -def generate_experiential_options_map(): - """Generate map of titles and options for experiential labels.""" - options_map = {} - # add experiential labels - options_map["experiential".title()] = generate_option_labels(ExperientialModelOutput) - return options_map - -def generate_interpersonal_options_map(): - """Generate map of titles and options for interpersonal labels.""" - options_map = {} - # add interpersonal labels - options_map["contact".title()] = generate_option_labels(ContactModelOutput) - options_map["angle".title()] = generate_option_labels(AngleModelOutput) - options_map["point of view".title()] = generate_option_labels(PointOfViewModelOutput) - options_map["distance".title()] = generate_option_labels(DistanceModelOutput) - options_map["modality lighting".title()] = generate_option_labels(ModalityLightingModelOutput) - options_map["modality color".title()] = generate_option_labels(ModalityColorModelOutput) - options_map["modality depth".title()] = generate_option_labels(ModalityDepthModelOutput) - return options_map - -def generate_textual_options_map(): - """Generate map of titles and options for textual labels.""" - options_map = {} - # add textual labels - options_map["information value".title()] = generate_option_labels(InformationValueModelOutput) - options_map["framing".title()] = generate_option_labels(FramingModelOutput) - options_map["salience".title()] = generate_option_labels(SalienceModelOutput) - return options_map - -def generate_body(): - image_container = dmc.Image( - width=600, - height=600, - withPlaceholder=True, - placeholder=[dmc.Loader(color="gray", size="md")], - ) - # prepare experiential container - experiential_map = generate_experiential_options_map() - experiential_container = dmc.Col( - children=[ - dmc.Container([ - html.H4(list(experiential_map.keys())[0]), - html.B("visual syntax".title()), - dcc.RadioItems(options=list(experiential_map.values())[0]), - ]) - ], span=4 - ) - # prepare interpersonal container - interpersonal_map = generate_interpersonal_options_map() - interpersonal_container = dmc.Col( - children=[ - html.H4("interpersonal".title()), - ], span=4 - ) - for title, options in interpersonal_map.items(): - interpersonal_container.children.append( - dmc.Container([ - html.B(title), - dcc.RadioItems(options) - ]) - ) - # prepare textual container - textual_map = generate_textual_options_map() - textual_container = dmc.Col( - children=[ - html.H4("textual".title()), - ], span=4 - ) - for title, options in textual_map.items(): - textual_container.children.append( - dmc.Container([ - html.B(title), - dcc.RadioItems(options) - ]) - ) - # prepare labels container - label_container = dmc.Grid( - children=[ - experiential_container, - interpersonal_container, - textual_container, - ], - ) - # build the full body container - body_container = dmc.Container( +body_element = dmc.Container( + fluid=True, + children=[ dmc.Grid( + grow=True, children=[ - dmc.Col( - dmc.Center( - image_container, - ), - span=5, - ), - dmc.Col( - # radio buttons part - children = [ - label_container, - dmc.Button( - "confirm", - id="submit-button", - fullWidth=True, - color="lime", - radius="sm", - size="md", - style={ - "height": "50px" - } - ), - ], span=7, - ), - # dmc.Col(span=1), - ], grow=True - ), fluid=True - ) - return body_container \ No newline at end of file + dmc.Col([image_element], span=5), + dmc.Col([inputs_element], span=7) + ], + ) + ], +) \ No newline at end of file diff --git a/src/web/layout/header.py b/src/web/layout/header.py index e1231c4..9e5b2cf 100644 --- a/src/web/layout/header.py +++ b/src/web/layout/header.py @@ -1,15 +1,21 @@ import dash_mantine_components as dmc from dash import html -def generate_header(): - header = dmc.Header( - height=80, - children=[ - dmc.Container( - children=[ - html.H2(children="Visual Critical Discourse Analysis Tool", style={"margin-left": "20px", "padding-top": "-5px"}), - ], size="xl", px="xl", - ), - ] - ) - return header \ No newline at end of file + +header_element = dmc.Header( + height=80, + children=[ + dmc.Container( + children=[ + html.H2( + children="Visual Critical Discourse Analysis Tool", + style={ + "margin-left": "20px", + "padding-top": "-5px" + }, + ), + ], + size="xl", px="xl", + ), + ] +) diff --git a/src/web/layout/image.py b/src/web/layout/image.py new file mode 100644 index 0000000..fa8ae21 --- /dev/null +++ b/src/web/layout/image.py @@ -0,0 +1,17 @@ +import dash_mantine_components as dmc + + +image_element = dmc.Center( + dmc.Image( + id="image_container", + width=600, + height=600, + withPlaceholder=True, + placeholder=[ + dmc.Loader( + color="gray", + size="md" + ) + ] + ) +) \ No newline at end of file diff --git a/src/web/layout/inputs.py b/src/web/layout/inputs.py new file mode 100644 index 0000000..e215ad5 --- /dev/null +++ b/src/web/layout/inputs.py @@ -0,0 +1,22 @@ +import dash_mantine_components as dmc + +from .labels import labels_element + +next_button = dmc.Button( + "next".title(), + id="next-button", + fullWidth=True, + color="lime", + radius="sm", + size="md", + style={ + "height": "50px" + } +) + +inputs_element = dmc.SimpleGrid( + children=[ + labels_element, + next_button + ] +) \ No newline at end of file diff --git a/src/web/layout/labels.py b/src/web/layout/labels.py new file mode 100644 index 0000000..54ce658 --- /dev/null +++ b/src/web/layout/labels.py @@ -0,0 +1,104 @@ +from dash import html, dcc +import dash_mantine_components as dmc +from typing import List + +from src.model_experiential import ExperientialModelOutput +from src.model_interpersonal import ( + ContactModelOutput, + AngleModelOutput, + PointOfViewModelOutput, + DistanceModelOutput, + ModalityLightingModelOutput, + ModalityColorModelOutput, + ModalityDepthModelOutput +) +from src.model_textual import ( + InformationValueModelOutput, + FramingModelOutput, + SalienceModelOutput +) + +def generate_option_labels(model) -> List[str]: + """Generate presentable list of attributes from an OutputModel.""" + labels = [ + label.replace('_', ' ').title() + for label in model.list_fields() + ] + return labels + +def generate_experiential_options_map(): + """Generate map of titles and options for experiential labels.""" + options_map = {} + # add experiential labels + options_map["experiential".title()] = generate_option_labels(ExperientialModelOutput) + return options_map + +def generate_interpersonal_options_map(): + """Generate map of titles and options for interpersonal labels.""" + options_map = {} + # add interpersonal labels + options_map["contact".title()] = generate_option_labels(ContactModelOutput) + options_map["angle".title()] = generate_option_labels(AngleModelOutput) + options_map["point of view".title()] = generate_option_labels(PointOfViewModelOutput) + options_map["distance".title()] = generate_option_labels(DistanceModelOutput) + options_map["modality lighting".title()] = generate_option_labels(ModalityLightingModelOutput) + options_map["modality color".title()] = generate_option_labels(ModalityColorModelOutput) + options_map["modality depth".title()] = generate_option_labels(ModalityDepthModelOutput) + return options_map + +def generate_textual_options_map(): + """Generate map of titles and options for textual labels.""" + options_map = {} + # add textual labels + options_map["information value".title()] = generate_option_labels(InformationValueModelOutput) + options_map["framing".title()] = generate_option_labels(FramingModelOutput) + options_map["salience".title()] = generate_option_labels(SalienceModelOutput) + return options_map + +# prepare experiential container +experiential_map = generate_experiential_options_map() +experiential_container = dmc.Col( + children=[ + dmc.Container([ + html.H4(list(experiential_map.keys())[0]), + html.B("visual syntax".title()), + dcc.RadioItems(options=list(experiential_map.values())[0]), + ]) + ], span=5 +) +# prepare interpersonal container +interpersonal_map = generate_interpersonal_options_map() +interpersonal_container = dmc.Col( + children=[ + html.H4("interpersonal".title()), + ], span=3 +) +for title, options in interpersonal_map.items(): + interpersonal_container.children.append( + dmc.Container([ + html.B(title), + dcc.RadioItems(options) + ]) + ) +# prepare textual container +textual_map = generate_textual_options_map() +textual_container = dmc.Col( + children=[ + html.H4("textual".title()), + ], span=4 +) +for title, options in textual_map.items(): + textual_container.children.append( + dmc.Container([ + html.B(title), + dcc.RadioItems(options) + ]) + ) + +labels_element = dmc.Grid( + children=[ + experiential_container, + interpersonal_container, + textual_container, + ] +) \ No newline at end of file diff --git a/src/web/layout/layout.py b/src/web/layout/layout.py new file mode 100644 index 0000000..7533366 --- /dev/null +++ b/src/web/layout/layout.py @@ -0,0 +1,35 @@ +from dash import dcc +import dash_mantine_components as dmc + +from .stores import stores_element +from .alerts import alerts_element +from .header import header_element +from .body import body_element + + +app_layout = dmc.MantineProvider( + theme={ + "fontFamily": '"Inter", sans-serif', + "components": { + "NavLink": { + "styles": { + "label": { + "color": "#c2c7d0" + } + } + } + }, + }, + children=[ + stores_element, + alerts_element, + dmc.Container( + children=[ + header_element, + body_element, + ], + fluid=True + ), + + ] +) diff --git a/src/web/layout/stores.py b/src/web/layout/stores.py new file mode 100644 index 0000000..6b2cad8 --- /dev/null +++ b/src/web/layout/stores.py @@ -0,0 +1,6 @@ +from dash import html, dcc + + +stores_element = html.Div([ + dcc.Store(id='visual-communication-name', storage_type='session'), +]) \ No newline at end of file