diff --git a/src/defaults.ini b/src/defaults.ini new file mode 100644 index 0000000..da256a1 --- /dev/null +++ b/src/defaults.ini @@ -0,0 +1,6 @@ +[main] +logger_level=debug + +[discord] +service_name=visual_critical_discourse_analysis +logger_level=warning diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..d362393 --- /dev/null +++ b/src/main.py @@ -0,0 +1,98 @@ +import logging +from discord_logging.handler import DiscordHandler +from dotenv import load_dotenv +from configparser import ConfigParser +from pathlib import Path +import logging +import os + +from web import app + +# load default values +config = ConfigParser() +config.read(Path(__file__).parent / 'defaults.ini') +LOGGER_LEVEL = config.get('main', 'logger_level') +DISCORD_SERVICE_NAME = config.get('discord', 'service_name') +DISCORD_LOGGER_LEVEL = config.get('discord', 'logger_level') + + +def setup_logging( + discord_webhook_url: str, + discord_service_name: str = DISCORD_SERVICE_NAME, + discord_logger_level: str = DISCORD_LOGGER_LEVEL, + logger_level: str = LOGGER_LEVEL, +) -> None: + # setup stream handler + fmt = ( + '%(asctime)s | ' + '%(levelname)s | ' + '%(filename)s | ' + '%(funcName)s | ' + '%(message)s' + ) + datefmt = '%Y-%m-%d %H:%M:%S' + level = getattr(logging, logger_level.upper()) + logging.basicConfig(format=fmt, datefmt=datefmt, level=level) + logger = logging.getLogger() + # add discord handler + discord_handler = DiscordHandler( + service_name=discord_service_name, + webhook_url=discord_webhook_url, + ) + discord_handler.setFormatter(logging.Formatter('%(message)s')) + level = getattr(logging, discord_logger_level.upper()) + discord_handler.setLevel(level=level) + logger.addHandler(discord_handler) + logging.debug('finished') + + +def initialise_app() -> None: + """ + Ensure all necessary environment variables are provided + """ + # load env vars + load_dotenv() + # ensure env vars set + necesasary_var_map = { + 'LOGGER_LEVEL': False, + 'DISCORD_SERVICE_NAME': False, + 'DISCORD_WEBHOOK_URL': True, + 'DISCORD_LOGGER_LEVEL': False, + 'DATABASE_HOST': True, + 'DATABASE_PORT': True, + 'DATABASE_ENGINE': True, + 'DATABASE_DATABASE': True, + 'DATABASE_USERNAME': True, + 'DATABASE_PASSWORD': True, + } + for env_var, must_be_set in necesasary_var_map.items(): + if must_be_set: + # ensure env var set + assert ( + env_var in os.environ + ), ( + f"environment variable not set: {env_var}" + ) + # set variable from env var + locals()[env_var.lower()] = os.getenv(key=env_var) + else: + # ensure default value set + assert env_var in globals(), f"default variable not set: {env_var}" + # set variable from env var with backup from default value + locals()[env_var.lower()] = os.getenv( + key=env_var, + default=globals()[env_var] + ) + setup_logging( + discord_webhook_url=locals()['discord_webhook_url'], + discord_service_name=locals()['discord_service_name'], + discord_logger_level=locals()['discord_logger_level'], + logger_level=locals()['logger_level'], + ) + logging.debug('finished') + + +if __name__ == "__main__": + # initialise_app() + app.run(debug=True) + logging.info("started app") \ No newline at end of file diff --git a/src/model_experiential/__init__.py b/src/model_experiential/__init__.py new file mode 100644 index 0000000..b0d4f72 --- /dev/null +++ b/src/model_experiential/__init__.py @@ -0,0 +1 @@ +from .output import model_labels \ No newline at end of file diff --git a/src/model_experiential/output.py b/src/model_experiential/output.py new file mode 100644 index 0000000..bddaf06 --- /dev/null +++ b/src/model_experiential/output.py @@ -0,0 +1,21 @@ + +model_labels = [ + "non transactional action", + "non transactional reaction", + "unidirectional transactional action", + "unidirectional transactional reaction", + "bidirectional transactional action", + "bidirectional transactional reaction", + "conversion", + "speech process", + "classification overt taxonomy", + "analytical exhaustive", + "analytical disarranged", + "analytical temporal", + "analytical distributed", + "anaytical topological", + "analytical exploded", + "analytical inclusive", + "symbolic suggestive", + "symbolic attributive" +] diff --git a/src/model_interpersonal/__init__.py b/src/model_interpersonal/__init__.py new file mode 100644 index 0000000..b0d4f72 --- /dev/null +++ b/src/model_interpersonal/__init__.py @@ -0,0 +1 @@ +from .output import model_labels \ No newline at end of file diff --git a/src/model_interpersonal/output.py b/src/model_interpersonal/output.py new file mode 100644 index 0000000..a95e916 --- /dev/null +++ b/src/model_interpersonal/output.py @@ -0,0 +1,36 @@ + +model_labels = { + "contact": [ + "contact offer", + "contact demand" + ], + "angle": [ + "high", + "eye-level", + "low" + ], + "point-of-view": [ + "frontal", + "oblique" + ], + "distance": [ + "long", + "medium", + "close" + ], + "modality lighting": [ + "high", + "medium", + "low" + ], + "modality color": [ + "high", + "medium", + "low" + ], + "modality depth": [ + "high", + "medium", + "low" + ] +} diff --git a/src/model_textual/__init__.py b/src/model_textual/__init__.py new file mode 100644 index 0000000..b0d4f72 --- /dev/null +++ b/src/model_textual/__init__.py @@ -0,0 +1 @@ +from .output import model_labels \ No newline at end of file diff --git a/src/model_textual/output.py b/src/model_textual/output.py new file mode 100644 index 0000000..8a4e08a --- /dev/null +++ b/src/model_textual/output.py @@ -0,0 +1,21 @@ + +model_labels = { + "information value": [ + "given-new", + "ideal-real", + "central-marginal" + ], + "framing": [ + "frame lines", + "empty space", + "colour contrast", + "form contrast" + ], + "salience": [ + "size", + "colour", + "tone", + "form", + "positioning" + ] +} diff --git a/src/web/__init__.py b/src/web/__init__.py new file mode 100644 index 0000000..2bbf8b9 --- /dev/null +++ b/src/web/__init__.py @@ -0,0 +1,4 @@ +from .app import ( + app, + server +) diff --git a/src/web/app.py b/src/web/app.py new file mode 100644 index 0000000..807b8b8 --- /dev/null +++ b/src/web/app.py @@ -0,0 +1,33 @@ +from dash import Dash, html, Input, Output, State, page_container, page_registry +import dash_bootstrap_components as dbc +import dash_mantine_components as dmc + +from .header import generate_header +from .body import generate_body +from model_experiential import model_labels as experiential_labels +from model_interpersonal import model_labels as interpersonal_labels +from model_textual import model_labels as textual_labels + + +app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) +server = app.server + +app.layout = dmc.MantineProvider( + theme={ + 'fontFamily': '"Inter", sans-serif', + "components": { + "NavLink":{'styles':{'label':{'color':'#c2c7d0'}}} + }, + }, + children=[ + dmc.Container([ + generate_header(), + generate_body( + experiential_labels, + interpersonal_labels, + textual_labels + ), + ]), + ], +) + \ No newline at end of file diff --git a/src/web/body.py b/src/web/body.py new file mode 100644 index 0000000..dbab304 --- /dev/null +++ b/src/web/body.py @@ -0,0 +1,71 @@ +import dash_mantine_components as dmc +from dash import dcc, html + +def generate_body( + experiential_labels, + interpersonal_labels, + textual_labels +): + image_container = dmc.Image( + width=400, + height=400, + withPlaceholder=True, + placeholder=[dmc.Loader(color="gray", size="md")], + ) + + experiential_labels_container = dmc.Container( + children=[ + html.H4("experiential labels".title()), + dcc.RadioItems(options=list(experiential_labels)), + ] + ) + + interpersonal_labels_container = dmc.Container( + children=[] + ) + for category, options in interpersonal_labels.items(): + interpersonal_labels_container.children.append(html.H4(category.title())) + interpersonal_labels_container.children.append(dcc.RadioItems(options)) + + textual_labels_container = dmc.Container( + children=[] + ) + for category, options in textual_labels.items(): + textual_labels_container.children.append(html.H4(category.title())) + textual_labels_container.children.append(dcc.RadioItems(options)) + + label_container = dmc.Container( + children=[ + experiential_labels_container, + dmc.Divider(), + interpersonal_labels_container, + dmc.Divider(), + textual_labels_container, + dmc.Divider(), + html.Button( + "confirm", + id="submit-button" + ) + ] + ) + + body_container = dmc.Container( + dmc.Grid( + children=[ + dmc.Col( + image_container, + span=5, + ), + dmc.Col( + dmc.Divider(orientation="vertical"), + span=1, + ), + dmc.Col( + # radio buttons part + label_container, + span=5, + ), + ], grow=True + ) + ) + return body_container \ No newline at end of file diff --git a/src/web/header.py b/src/web/header.py new file mode 100644 index 0000000..e1231c4 --- /dev/null +++ b/src/web/header.py @@ -0,0 +1,15 @@ +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