Skip to content

Commit

Permalink
Use the config module to access app config
Browse files Browse the repository at this point in the history
  • Loading branch information
mmwinther committed Jan 25, 2024
1 parent 52ce768 commit d96973a
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project specific ignores
tests/resources/*.json
src/datadoc/.env.dev
src/datadoc/.env

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": true,
"ruff.lint.run": "onType",
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
}
}
}
1 change: 0 additions & 1 deletion src/datadoc/.env.default

This file was deleted.

35 changes: 14 additions & 21 deletions src/datadoc/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from __future__ import annotations

import logging
import os
from pathlib import Path

import dash_bootstrap_components as dbc
from dash import Dash
from flask_healthz import healthz

from datadoc import config
from datadoc import state
from datadoc.backend.datadoc_metadata import DataDocMetadata
from datadoc.enums import SupportedLanguages
Expand All @@ -31,12 +31,9 @@
from datadoc.utils import pick_random_port
from datadoc.utils import running_in_notebook

logging.basicConfig(level=config.get_log_level(), force=True)
logger = logging.getLogger(__name__)

NAME = "Datadoc"
DEFAULT_PORT: int = 7002
JUPYTERHUB_SERVICE_PREFIX_ENV = "JUPYTERHUB_SERVICE_PREFIX"


def build_app(app: type[Dash]) -> Dash:
"""Define the layout, register callbacks."""
Expand Down Expand Up @@ -75,24 +72,23 @@ def build_app(app: type[Dash]) -> Dash:

def get_app(dataset_path: str | None = None) -> tuple[Dash, int]:
"""Centralize all the ugliness around initializing the app."""
logging.basicConfig(level=logging.INFO, force=True)
logger.info("Datadoc version v%s", get_app_version())
state.current_metadata_language = SupportedLanguages.NORSK_BOKMÅL
state.metadata = DataDocMetadata(dataset_path)

# This must be set to run correctly on Dapla Jupyter
if JUPYTERHUB_SERVICE_PREFIX_ENV in os.environ:
# The service prefix must be set to run correctly on Dapla Jupyter
if prefix := config.get_jupyterhub_service_prefix():
port = pick_random_port()
requests_pathname_prefix = (
f"{os.getenv(JUPYTERHUB_SERVICE_PREFIX_ENV, '/')}proxy/{port}/"
)
requests_pathname_prefix = f"{prefix}proxy/{port}/"
else:
port = DEFAULT_PORT
port = config.get_port()
requests_pathname_prefix = "/"

name = config.get_app_name()

app = Dash(
name=NAME,
title=NAME,
name=name,
title=name,
assets_folder=f"{Path(__file__).parent}/assets",
requests_pathname_prefix=requests_pathname_prefix,
)
Expand All @@ -110,22 +106,19 @@ def get_app(dataset_path: str | None = None) -> tuple[Dash, int]:

def main(dataset_path: str | None = None) -> None:
"""Entrypoint when running as a script."""
logging.basicConfig(level=logging.DEBUG, force=True)
logger.info("Starting app with dataset_path = %s", dataset_path)
if dataset_path:
logger.info("Starting app with dataset_path = %s", dataset_path)
app, port = get_app(dataset_path)
if running_in_notebook():
logger.info("Running in notebook")
app.run(
jupyter_mode="tab",
jupyter_server_url=os.getenv("JUPYTERHUB_HTTP_REFERER", None),
jupyter_server_url=config.get_jupyterhub_http_referrer(),
jupyter_height=1000,
port=port,
)
else:
# Assume running in server mode is better (largely for development purposes)
logging.basicConfig(level=logging.DEBUG, force=True)
logger.debug("Starting in development mode")
app.run(debug=True, port=port)
app.run(debug=config.get_dash_development_mode(), port=port)


if __name__ == "__main__":
Expand Down
73 changes: 67 additions & 6 deletions src/datadoc/config.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,78 @@
"""Centralised configuration management for Datadoc."""
from __future__ import annotations

import logging
import os
from pprint import pformat

from dotenv import dotenv_values
from dotenv import load_dotenv

_config: dict[str, str | None] = {
**dotenv_values(".env.default"), # load default config
**dotenv_values(".env.dev"), # load local dev config
**os.environ, # override loaded values with environment variables
}
logging.basicConfig(level=logging.DEBUG, force=True)

logger = logging.getLogger(__name__)

load_dotenv()

logger.info("Loaded .env file config: \n%s", pformat(dict(dotenv_values().items())))


def _get_config_item(item: str) -> str | None:
"""Get a config item. Makes sure all access is logged."""
value = os.getenv(item)
logger.debug("Accessed config. Name: %s, Value: %s", item, value)
return value


def get_jupyterhub_user() -> str | None:
"""Get the JupyterHub user name."""
return _config.get("JUPYTERHUB_USER")
return _get_config_item("JUPYTERHUB_USER")


def get_datadoc_dataset_path() -> str | None:
"""Get the path to the dataset."""
return _get_config_item("DATADOC_DATASET_PATH")


def get_log_level() -> int:
"""Get the log level."""
# Magic numbers as defined in Python's stdlib logging
log_levels: dict[str, int] = {
"CRITICAL": 50,
"ERROR": 40,
"WARNING": 30,
"INFO": 20,
"DEBUG": 10,
}
if level_string := _get_config_item("DATADOC_LOG_LEVEL"):
try:
return log_levels[level_string.upper()]
except KeyError:
return log_levels["INFO"]
else:
return log_levels["INFO"]


def get_dash_development_mode() -> bool | None:
"""Get the development mode for Dash."""
return _get_config_item("DATADOC_DASH_DEVELOPMENT_MODE") == "True"


def get_jupyterhub_service_prefix() -> str | None:
"""Get the JupyterHub service prefix."""
return _get_config_item("JUPYTERHUB_SERVICE_PREFIX")


def get_app_name() -> str:
"""Get the name of the app. Defaults to 'Datadoc'."""
return _get_config_item("DATADOC_APP_NAME") or "Datadoc"


def get_jupyterhub_http_referrer() -> str | None:
"""Get the JupyterHub http referrer."""
return _get_config_item("JUPYTERHUB_HTTP_REFERER")


def get_port() -> int:
"""Get the port to run the app on."""
return int(_get_config_item("DATADOC_PORT") or 7002)
9 changes: 3 additions & 6 deletions src/datadoc/frontend/callbacks/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from __future__ import annotations

import logging
import os
from typing import TYPE_CHECKING
from typing import TypeAlias

from datadoc_model import model

from datadoc import config
from datadoc import enums
from datadoc import state
from datadoc.backend.datadoc_metadata import METADATA_DOCUMENT_FILE_SUFFIX
Expand All @@ -25,8 +25,6 @@
logger = logging.getLogger(__name__)


DATADOC_DATASET_PATH_ENV_VAR = "DATADOC_DATASET_PATH"

MetadataInputTypes: TypeAlias = str | list[str] | int | float | bool | None


Expand Down Expand Up @@ -89,10 +87,9 @@ def get_dataset_path() -> str | Path | None:
"""Extract the path to the dataset from the potential sources."""
if state.metadata.dataset is not None:
return state.metadata.dataset
path_from_env = os.getenv(DATADOC_DATASET_PATH_ENV_VAR)
path_from_env = config.get_datadoc_dataset_path()
logger.info(
"Dataset path from %s: '%s'",
DATADOC_DATASET_PATH_ENV_VAR,
"Dataset path from env var: '%s'",
path_from_env,
)
return path_from_env
Expand Down

0 comments on commit d96973a

Please sign in to comment.