Skip to content

Commit

Permalink
Added dataset input field that uses ssb input (#226)
Browse files Browse the repository at this point in the history
* Add context to Backstage doc (#197)

* Use a with block for ThreadPoolExecutor (#222)

* Release v0.8.1 (#224)

* Added dataset input field that uses ssb input

* Updated builder for dataset input filed

* Add DatasetDropdownField - change datatype DISPLAY_DATASET - temp commented out code

* Change datatypes for Datasetidentifiers - som dropdowns ok - test red

* DatasetDisplayTypes list

* Dropdown options ok dataset

* remove comment

* Possible Const id for dataset

* Changed tests to be compatible with new display dataset functions

---------

Co-authored-by: Miles Mason Winther <[email protected]>
Co-authored-by: rlj <[email protected]>
Co-authored-by: tilen1976 <[email protected]>
  • Loading branch information
4 people authored Mar 15, 2024
1 parent e0769d8 commit e09b641
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 170 deletions.
3 changes: 1 addition & 2 deletions src/datadoc/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from datadoc.frontend.components.control_bars import build_language_dropdown
from datadoc.frontend.components.control_bars import header
from datadoc.frontend.components.control_bars import progress_bar
from datadoc.frontend.components.dataset_tab import build_dataset_tab
from datadoc.frontend.components.dataset_tab import build_new_dataset_tab
from datadoc.frontend.components.variables_tab import build_variables_tab
from datadoc.logging.logging_config import configure_logging
Expand All @@ -43,7 +42,7 @@
def build_app(app: type[Dash]) -> Dash:
"""Define the layout, register callbacks."""
tabs_children = [
build_dataset_tab(),
# build_dataset_tab(),
build_variables_tab(),
build_new_dataset_tab(),
]
Expand Down
48 changes: 30 additions & 18 deletions src/datadoc/frontend/callbacks/register_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from dash import Output
from dash import State
from dash import ctx
from dash import no_update

from datadoc import state
from datadoc.enums import SupportedLanguages
Expand All @@ -30,12 +29,14 @@
from datadoc.frontend.components.builders import build_ssb_accordion
from datadoc.frontend.components.dataset_tab import DATASET_METADATA_INPUT
from datadoc.frontend.components.dataset_tab import SECTION_WRAPPER_ID
from datadoc.frontend.components.dataset_tab import build_dataset_metadata_accordion
from datadoc.frontend.components.variables_tab import ACCORDION_WRAPPER_ID
from datadoc.frontend.components.variables_tab import VARIABLES_INFORMATION_ID
from datadoc.frontend.fields.display_base import VARIABLES_METADATA_DATE_INPUT
from datadoc.frontend.fields.display_base import VARIABLES_METADATA_INPUT
from datadoc.frontend.fields.display_dataset import DISPLAYED_DROPDOWN_DATASET_METADATA
from datadoc.frontend.fields.display_dataset import NON_EDITABLE_DATASET_METADATA
from datadoc.frontend.fields.display_dataset import OBLIGATORY_EDITABLE_DATASET_METADATA
from datadoc.frontend.fields.display_dataset import OPTIONAL_DATASET_METADATA
from datadoc.frontend.fields.display_variables import OBLIGATORY_VARIABLES_METADATA
from datadoc.frontend.fields.display_variables import OPTIONAL_VARIABLES_METADATA
from datadoc.frontend.fields.display_variables import VariableIdentifiers
Expand Down Expand Up @@ -149,20 +150,19 @@ def callback_open_dataset(
"""
return open_dataset_handling(n_clicks, dataset_path)

@app.callback(
Output("dataset-accordion", "children"),
Input("open-button", "n_clicks"),
prevent_initial_call=True,
)
def callback_clear_accordion_values(n_clicks: int) -> list[dbc.AccordionItem]:
"""Recreate accordion items with unique IDs.
# @app.callback(
# Output("dataset-accordion", "children"), # noqa: ERA001
# Input("open-button", "n_clicks"), # noqa: ERA001
# prevent_initial_call=True, # noqa: ERA001)
# def callback_clear_accordion_values(n_clicks: int) -> list[dbc.AccordionItem]:
# """Recreate accordion items with unique IDs.

The purpose is to avoid browser caching and clear the values of all
components inside the dataset accordion when new file is opened
"""
if n_clicks and n_clicks > 0:
return build_dataset_metadata_accordion(n_clicks)
return no_update
# The purpose is to avoid browser caching and clear the values of all
# components inside the dataset accordion when new file is opened
# """
# if n_clicks and n_clicks > 0:
# return build_dataset_metadata_accordion(n_clicks) # noqa: ERA001
# return no_update # noqa: ERA001

@app.callback(
Output(VARIABLES_INFORMATION_ID, "children"),
Expand Down Expand Up @@ -222,9 +222,21 @@ def callback_populate_dataset_workspace(language: str) -> list:
update_global_language_state(SupportedLanguages(language))
logger.info("Populating new dataset workspace")
return [
build_dataset_edit_section("Obligatorisk"),
build_dataset_edit_section("Anbefalt"),
build_dataset_edit_section("Maskingenerert"),
build_dataset_edit_section(
"Obligatorisk",
OBLIGATORY_EDITABLE_DATASET_METADATA,
language,
),
build_dataset_edit_section(
"Anbefalt",
OPTIONAL_DATASET_METADATA,
language,
),
build_dataset_edit_section(
"Maskingenerert",
NON_EDITABLE_DATASET_METADATA,
language,
),
]

@app.callback(
Expand Down
27 changes: 23 additions & 4 deletions src/datadoc/frontend/components/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
from dash import html

from datadoc.frontend.fields.display_base import VARIABLES_METADATA_INPUT
from datadoc.frontend.fields.display_base import DatasetFieldTypes
from datadoc.frontend.fields.display_base import VariablesFieldTypes

if TYPE_CHECKING:
from datadoc_model import model

DATASET_METADATA_INPUT = "dataset-metadata-input"


class AlertTypes(Enum):
"""Types of alerts."""
Expand Down Expand Up @@ -134,11 +137,23 @@ def build_input_field_section(
)


def build_dataset_input_field_section() -> dbc.Form:
def build_dataset_input_field_section(
metadata_fields: list[DatasetFieldTypes],
language: str,
) -> dbc.Form:
"""Create input fields for dataset."""
return dbc.Form(
[
ssb.Input(label="SSB input"),
i.render(
{
"type": "NEW_DATASET",
# "type": DATASET_METADATA_INPUT, # noqa: ERA001
"id": i.identifier,
},
language,
)
for i in metadata_fields
# for i in OBLIGATORY_EDITABLE_DATASET_METADATA
],
id="new-dataset-form",
className="dataset-input-group",
Expand All @@ -162,13 +177,17 @@ def build_edit_section(
)


def build_dataset_edit_section(title: str) -> html.Section:
def build_dataset_edit_section(
title: str,
metadata_inputs: list,
language: str,
) -> html.Section:
"""Create edit section for dataset."""
return html.Section(
id={"type": "dataset-edit-section", "title": title},
children=[
ssb.Title(title, size=3, className="input-section-title"),
build_dataset_input_field_section(),
build_dataset_input_field_section(metadata_inputs, language),
],
className="dataset-edit-section",
)
Expand Down
87 changes: 5 additions & 82 deletions src/datadoc/frontend/components/dataset_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,97 +2,20 @@

from __future__ import annotations

import dash_bootstrap_components as dbc
from typing import TYPE_CHECKING

import ssb_dash_components as ssb
from dash import html

from datadoc.frontend.components.builders import build_ssb_styled_tab
from datadoc.frontend.fields.display_dataset import NON_EDITABLE_DATASET_METADATA
from datadoc.frontend.fields.display_dataset import OBLIGATORY_EDITABLE_DATASET_METADATA
from datadoc.frontend.fields.display_dataset import OPTIONAL_DATASET_METADATA
from datadoc.frontend.fields.display_dataset import DisplayDatasetMetadata

if TYPE_CHECKING:
import dash_bootstrap_components as dbc

DATASET_METADATA_INPUT = "dataset-metadata-input"
SECTION_WRAPPER_ID = "section-wrapper-id"


def build_dataset_metadata_accordion_item(
title: str,
metadata_inputs: list[DisplayDatasetMetadata],
accordion_item_id: str,
) -> dbc.AccordionItem:
"""Build a Dash AccordionItem for the given Metadata inputs with a unique ID.
Typically used to categorize metadata fields.
"""
return dbc.AccordionItem(
title=title,
id=accordion_item_id,
children=[
dbc.Row(
[
dbc.Col(html.Label(i.display_name)),
dbc.Col(
i.component(
placeholder=i.description,
disabled=not i.editable,
id={
"type": DATASET_METADATA_INPUT,
"id": i.identifier,
},
**i.extra_kwargs,
),
width=5,
),
dbc.Col(width=4),
],
)
for i in metadata_inputs
],
)


def build_dataset_metadata_accordion(n_clicks: int = 0) -> list[dbc.AccordionItem]:
"""Build the accordion on Dataset metadata tab.
n_clicks parameter is appended to the accordions' items id
to avoid browser caching and refresh the values
"""
obligatory = build_dataset_metadata_accordion_item(
"Obligatorisk",
OBLIGATORY_EDITABLE_DATASET_METADATA,
accordion_item_id=f"obligatory-metadata-accordion-item-{n_clicks}",
)
optional = build_dataset_metadata_accordion_item(
"Anbefalt",
OPTIONAL_DATASET_METADATA,
accordion_item_id=f"optional-metadata-accordion-item-{n_clicks}",
)
non_editable = build_dataset_metadata_accordion_item(
"Maskingenerert",
NON_EDITABLE_DATASET_METADATA,
accordion_item_id=f"non-editable-metadata-accordion-item-{n_clicks}",
)
return [obligatory, optional, non_editable]


def build_dataset_tab() -> dbc.Tab:
"""Build the Dataset metadata tab."""
return build_ssb_styled_tab(
"Datasett",
dbc.Container(
[
dbc.Row(html.H2("Datasett detaljer", className="ssb-title")),
dbc.Accordion(
id="dataset-accordion",
always_open=True,
children=build_dataset_metadata_accordion(),
),
],
),
)


def build_new_dataset_tab() -> dbc.Tab:
"""Build the Dataset metadata tab."""
return build_ssb_styled_tab(
Expand Down
59 changes: 59 additions & 0 deletions src/datadoc/frontend/fields/display_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,63 @@ class DisplayDatasetMetadata(DisplayMetadata):
value_getter: Callable[[BaseModel, str], Any] = get_metadata_and_stringify


@dataclass
class DatasetInputField(DisplayMetadata):
"""Controls for how a given metadata field should be displayed.
Specific to dataset fields.
"""

extra_kwargs: dict[str, Any] = field(default_factory=input_kwargs_factory)
value_getter: Callable[[BaseModel, str], Any] = get_metadata_and_stringify
type: str = "text"

def render(
self,
dataset_id: dict,
language: str, # noqa: ARG002
# dataset: model.Dataset,
) -> ssb.Input:
"""Build input component."""
# value = self.value_getter(dataset, self.identifier) # noqa: ERA001
return ssb.Input(
label=self.display_name,
id=dataset_id,
debounce=True,
type=self.type,
disabled=not self.editable,
# value=value, # noqa: ERA001
className="variable-input",
)


@dataclass
class DatasetDropdownField(DisplayMetadata):
"""Control how a Dropdown should be displayed."""

extra_kwargs: dict[str, Any] = field(default_factory=empty_kwargs_factory)
value_getter: Callable[[BaseModel, str], Any] = get_metadata_and_stringify
# fmt: off
options_getter: Callable[[SupportedLanguages], list[dict[str, str]]] = lambda _: [] # noqa: E731
# fmt: on

def render(
self,
variable_id: dict,
language: str,
# variable: model.Variable,
) -> ssb.Dropdown:
"""Build Dropdown component."""
# value = self.value_getter(variable, self.identifier) # noqa: ERA001
return ssb.Dropdown(
header=self.display_name,
id=variable_id,
items=self.options_getter(SupportedLanguages(language)),
# value=value, # noqa: ERA001
className="dataset-dropdown",
)


@dataclass
class DisplayDatasetMetadataDropdown(DisplayDatasetMetadata):
"""Include the possible options which a user may choose from."""
Expand Down Expand Up @@ -260,3 +317,5 @@ def render(
| VariablesCheckboxField
| VariablesPeriodField
)

DatasetFieldTypes = DatasetInputField | DatasetDropdownField
Loading

0 comments on commit e09b641

Please sign in to comment.